diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000000..f300cbd721967 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,137 @@ +name: CI + +on: + push: + branches: + - swiftwasm + pull_request: + branches: + - swiftwasm + +jobs: + linux_build: + timeout-minutes: 0 + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v1 + - name: Build Linux installable archive + run: ./utils/webassembly/ci-linux.sh + - name: Upload Linux installable archive + uses: actions/upload-artifact@v1 + with: + name: linux-installable + path: ../swiftwasm-linux.tar.gz + - name: Pack test results + run: tar cJf swift-test-results.tar.gz ../build/*/swift-linux-x86_64/swift-test-results + - name: Upload test results + uses: actions/upload-artifact@v1 + with: + name: linux-test-results + path: ./swift-test-results.tar.gz + + macos_build: + timeout-minutes: 0 + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + - name: Build macOS installable archive + run: ./utils/webassembly/ci-mac.sh + - name: Upload macOS installable archive + uses: actions/upload-artifact@v1 + with: + name: macos-installable + path: ../swiftwasm-macos.tar.gz + - name: Upload packaging scripts + uses: actions/upload-artifact@v1 + with: + name: packaging-scripts + path: utils/webassembly + - name: Pack test results + run: tar cJf swift-test-results.tar.gz ../build/*/swift-macosx-x86_64/swift-test-results + - name: Upload test results + uses: actions/upload-artifact@v1 + with: + name: macos-test-results + path: ./swift-test-results.tar.gz + package: + name: Build SwiftWasm packages + needs: + - linux_build + - macos_build + runs-on: ubuntu-18.04 + steps: + - name: Download installable Linux archive + uses: actions/download-artifact@v1 + with: + name: linux-installable + - name: Download installable macOS archive + uses: actions/download-artifact@v1 + with: + name: macos-installable + - name: Download packaging scripts + uses: actions/download-artifact@v1 + with: + name: packaging-scripts + - name: Build the packages + shell: bash + run: | + cd packaging-scripts + find . -name '*.sh' -exec chmod +x {} \; + chmod +x sdkroot/swiftwasm + ./download-prebuilts.sh + + cp ../linux-installable/swiftwasm-linux.tar.gz \ + ../macos-installable/swiftwasm-macos.tar.gz \ + prebuilt + ./build-packages.sh + + cd output + tar xf swiftwasm-sdk-linux.tar.xz && echo "Successfully unpacked Linux SDK" + + cd swiftwasm-sdk + ./swiftwasm example/hello.swift hello.wasm && echo "Successfully linked hello.wasm" + + - name: Upload macOS package + uses: actions/upload-artifact@v1 + with: + name: macos-package + path: packaging-scripts/output/swiftwasm-sdk-macos.tar.xz + + - name: Upload Linux package + uses: actions/upload-artifact@v1 + with: + name: linux-package + path: packaging-scripts/output/swiftwasm-sdk-linux.tar.xz + + - name: Upload hello.wasm compiled with Linux package + uses: actions/upload-artifact@v1 + with: + name: linux-hello.wasm + path: packaging-scripts/output/swiftwasm-sdk/hello.wasm + + macos_smoke_test: + name: Compile hello.swift on macOS + runs-on: macos-latest + needs: package + steps: + - name: Download SwiftWasm macOS package + uses: actions/download-artifact@v1 + with: + name: macos-package + + - name: Build hello.wasm + shell: bash + run: | + cd macos-package + tar xf swiftwasm-sdk-macos.tar.xz && echo "Successfully unpacked macOS SDK" + + cd swiftwasm-sdk + ./swiftwasm example/hello.swift hello.wasm && echo "Successfully linked hello.wasm" + + - name: Upload hello.wasm compiled with macOS package + uses: actions/upload-artifact@v1 + with: + name: macos-hello.wasm + path: macos-package/swiftwasm-sdk/hello.wasm diff --git a/.gitignore b/.gitignore index 787b28d3d77f3..4aae0bfe05e29 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ docs/_build # Visual Studio metadata .vs +# clangd +.clangd + #==============================================================================# # Ignore CMake temporary files #==============================================================================# diff --git a/.mailmap b/.mailmap index a5478fcbb3ffd..45ac9d3125396 100644 --- a/.mailmap +++ b/.mailmap @@ -8,6 +8,7 @@ Amr Aboelela Ankit Aggarwal Argyrios Kyrtzidis Arsen Gasparyan +Ashley Garland Ben Cohen Ben Cohen Ben Cohen diff --git a/CMakeLists.txt b/CMakeLists.txt index 7490e1df821d0..460a6349515ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,11 +255,11 @@ set(SWIFT_ANDROID_DEPLOY_DEVICE_PATH "" CACHE STRING "Path on an Android device where build products will be pushed. These are used when running the test suite against the device") # -# User-configurable ICU specific options for Android, FreeBSD, Linux and Haiku. +# User-configurable ICU specific options for Android, FreeBSD, Linux, Haiku, and WebAssembly. # -foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU) - foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;x86_64) +foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU;WASI) + foreach(arch aarch64;armv6;armv7;i686;powerpc64;powerpc64le;s390x;wasm32;x86_64) set(SWIFT_${sdk}_${arch}_ICU_UC "" CACHE STRING "Path to a directory containing the icuuc library for ${sdk}") set(SWIFT_${sdk}_${arch}_ICU_UC_INCLUDE "" CACHE STRING @@ -633,6 +633,8 @@ else() set(SWIFT_HOST_VARIANT_ARCH_default "powerpc64le") elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x") set(SWIFT_HOST_VARIANT_ARCH_default "s390x") + elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "wasm32") + set(SWIFT_HOST_VARIANT_ARCH_default "wasm32") # FIXME: Only matches v6l/v7l - by far the most common variants elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l") set(SWIFT_HOST_VARIANT_ARCH_default "armv6") @@ -817,6 +819,18 @@ if(swift_build_windows AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") configure_sdk_windows("Windows" "msvc" "${SWIFT_SDK_WINDOWS_ARCHITECTURES}") endif() +# Should we cross-compile the standard library for WebAssembly (WASI)? +is_sdk_requested(WASI swift_build_wasm) +if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASI") + if(SWIFT_BUILD_DYNAMIC_SDK_OVERLAY OR SWIFT_BUILD_DYNAMIC_STDLIB) + message(FATAL_ERROR "Unable to build dynamic overlay/stdlib for WASI. WASM does not support dynamic linking.") + endif() + if("${SWIFT_SDK_WASI_ARCHITECTURES}" STREQUAL "") + set(SWIFT_SDK_WASI_ARCHITECTURES wasm32) + endif() + configure_sdk_unix("WASI" "${SWIFT_SDK_WASI_ARCHITECTURES}") +endif() + if("${SWIFT_SDKS}" STREQUAL "") set(SWIFT_SDKS "${SWIFT_CONFIGURED_SDKS}") endif() @@ -924,14 +938,24 @@ else() find_package(LibXml2) endif() -# You need libedit linked in order to check if you have el_wgets. -cmake_push_check_state() -list(APPEND CMAKE_REQUIRED_LIBRARIES "edit") -check_symbol_exists(el_wgets "histedit.h" HAVE_EL_WGETS) -if(HAVE_EL_WGETS) - set(HAVE_UNICODE_LIBEDIT 1) +if(LLVM_ENABLE_LIBEDIT) + find_package(LibEdit REQUIRED) +else() + find_package(LibEdit) +endif() + +if(LibEdit_FOUND) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES ${LibEdit_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LibEdit_LIBRARIES}) + check_symbol_exists(el_wgets "histedit.h" HAVE_EL_WGETS) + if(HAVE_EL_WGETS) + set(LibEdit_HAS_UNICODE YES) + else() + set(LibEdit_HAS_UNICODE NO) + endif() + cmake_pop_check_state() endif() -cmake_pop_check_state() check_symbol_exists(wait4 "sys/wait.h" HAVE_WAIT4) @@ -953,10 +977,16 @@ if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) set(SWIFT_LIBDISPATCH_CXX_COMPILER ${CMAKE_CXX_COMPILER}) elseif(${CMAKE_SYSTEM_NAME} STREQUAL ${CMAKE_HOST_SYSTEM_NAME}) if(CMAKE_SYSTEM_NAME STREQUAL Windows) - set(SWIFT_LIBDISPATCH_C_COMPILER - $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) - set(SWIFT_LIBDISPATCH_CXX_COMPILER - $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR AND + TARGET clang) + set(SWIFT_LIBDISPATCH_C_COMPILER + $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + set(SWIFT_LIBDISPATCH_CXX_COMPILER + $/clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + else() + set(SWIFT_LIBDISPATCH_C_COMPILER clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + set(SWIFT_LIBDISPATCH_CXX_COMPILER clang-cl${CMAKE_EXECUTABLE_SUFFIX}) + endif() else() set(SWIFT_LIBDISPATCH_C_COMPILER $/clang) set(SWIFT_LIBDISPATCH_CXX_COMPILER $/clang++) diff --git a/README.md b/README.md index 424c27e2e3daa..3d998e0197640 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Please make sure you use Python 2.x. Python 3.x is not supported currently. #### macOS -To build for macOS, you need [Xcode 11.2](https://developer.apple.com/xcode/downloads/). +To build for macOS, you need [Xcode 11.3](https://developer.apple.com/xcode/downloads/). The required version of Xcode changes frequently, and is often a beta release. Check this document or the host information on for the current required version. diff --git a/benchmark/scripts/Benchmark_DTrace.in b/benchmark/scripts/Benchmark_DTrace.in index dc50118bbbcc6..9b823008136dd 100644 --- a/benchmark/scripts/Benchmark_DTrace.in +++ b/benchmark/scripts/Benchmark_DTrace.in @@ -70,7 +70,7 @@ class DTraceBenchmarkDriver(perf_test_driver.BenchmarkDriver): def __init__(self, binary, xfail_list, csv_output): perf_test_driver.BenchmarkDriver.__init__( self, binary, xfail_list, - enable_parallel=False, + enable_parallel=True, opt_levels=['O']) self.csv_output = csv_output @@ -86,11 +86,14 @@ class DTraceBenchmarkDriver(perf_test_driver.BenchmarkDriver): sys.stdout.flush() def get_results_with_iters(iters): + e = os.environ + e['SWIFT_DETERMINISTIC_HASHING'] = '1' p = subprocess.Popen([ 'sudo', 'dtrace', '-s', DTRACE_PATH, - '-c', '%s %s %s' % (data['path'], data['test_name'], - '--num-iters=%d' % iters) - ], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')) + '-c', '%s %s %s %s' % (data['path'], data['test_name'], + '--num-iters=%d' % iters, + '--num-samples=2') + ], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w'), env=e) results = [x for x in p.communicate()[0].split("\n") if len(x) > 0] return [ x.split(',')[1] for x in diff --git a/benchmark/utils/DriverUtils.swift b/benchmark/utils/DriverUtils.swift index 351d599e0fb20..13557d0158875 100644 --- a/benchmark/utils/DriverUtils.swift +++ b/benchmark/utils/DriverUtils.swift @@ -412,9 +412,10 @@ final class TestRunner { private static func getExecutedInstructions() -> UInt64 { if #available(OSX 10.9, iOS 7.0, *) { var u = rusage_info_v4() - let p = UnsafeMutablePointer(&u) - p.withMemoryRebound(to: Optional.self, capacity: 1) { up in - let _ = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, up) + withUnsafeMutablePointer(to: &u) { p in + p.withMemoryRebound(to: Optional.self, capacity: 1) { up in + let _ = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, up) + } } return u.ri_instructions } else { diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index b40bd4d881ed4..ec712098bac22 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -332,6 +332,8 @@ function(_add_variant_c_compile_flags) list(APPEND result -isystem;${path}) endforeach() list(APPEND result "-D__ANDROID_API__=${SWIFT_ANDROID_API_LEVEL}") + elseif("${CFLAGS_SDK}" STREQUAL "WASI") + list(APPEND result "-D_WASI_EMULATED_MMAN") elseif(CFLAGS_SDK STREQUAL WINDOWS) swift_windows_include_for_arch(${CFLAGS_ARCH} ${CFLAGS_ARCH}_INCLUDE) foreach(path ${${CFLAGS_ARCH}_INCLUDE}) @@ -391,6 +393,8 @@ function(_add_variant_swift_compile_flags foreach(path IN LISTS ${arch}_swift_include) list(APPEND result "\"${CMAKE_INCLUDE_FLAG_C}${path}\"") endforeach() + elseif("${sdk}" STREQUAL "WASI") + list(APPEND result "-Xcc" "-D_WASI_EMULATED_MMAN") endif() if(NOT BUILD_STANDALONE) @@ -500,6 +504,8 @@ function(_add_variant_link_flags) foreach(path IN LISTS ${LFLAGS_ARCH}_LIB) list(APPEND library_search_directories ${path}) endforeach() + elseif("${LFLAGS_SDK}" STREQUAL "WASI") + list(APPEND result "-Wl,wasi-emulated-mman") else() # If lto is enabled, we need to add the object path flag so that the LTO code # generator leaves the intermediate object file in a place where it will not @@ -996,7 +1002,8 @@ function(_add_swift_library_single target name) ${INCORPORATED_OBJECT_LIBRARIES_EXPRESSIONS} ${SWIFTLIB_SINGLE_XCODE_WORKAROUND_SOURCES}) if(("${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF") AND + "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF" OR + "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "WASM") AND SWIFTLIB_SINGLE_TARGET_LIBRARY) if("${libkind}" STREQUAL "SHARED" AND NOT SWIFTLIB_SINGLE_NOSWIFTRT) # TODO(compnerd) switch to the generator expression when cmake is upgraded @@ -1315,6 +1322,12 @@ function(_add_swift_library_single target name) list(APPEND c_compile_flags -D_WINDLL) endif() endif() + # Double-check that we're not trying to build a dynamic library for WASM. + if(SWIFTLIB_SINGLE_SDK MATCHES WASM) + if(libkind STREQUAL SHARED) + message(FATAL_ERROR "WASM does not support shared libraries.") + endif() + endif() _add_variant_link_flags( SDK "${SWIFTLIB_SINGLE_SDK}" ARCH "${SWIFTLIB_SINGLE_ARCHITECTURE}" @@ -1612,6 +1625,9 @@ endfunction() # SWIFT_MODULE_DEPENDS_HAIKU # Swift modules this library depends on when built for Haiku. # +# SWIFT_MODULE_DEPENDS_WASI +# Swift modules this library depends on when built for WASI. +# # FRAMEWORK_DEPENDS # System frameworks this library depends on. # @@ -1720,7 +1736,9 @@ function(add_swift_target_library name) SWIFT_MODULE_DEPENDS_OSX SWIFT_MODULE_DEPENDS_TVOS SWIFT_MODULE_DEPENDS_WATCHOS + SWIFT_MODULE_DEPENDS_WASI SWIFT_MODULE_DEPENDS_WINDOWS + SWIFT_MODULE_DEPENDS_FROM_SDK TARGET_SDKS) cmake_parse_arguments(SWIFTLIB @@ -1829,6 +1847,9 @@ function(add_swift_target_library name) elseif(${sdk} STREQUAL HAIKU) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_HAIKU}) + elseif(${sdk} STREQUAL WASI) + list(APPEND swiftlib_module_depends_flattened + ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WASI}) elseif(${sdk} STREQUAL WINDOWS) list(APPEND swiftlib_module_depends_flattened ${SWIFTLIB_SWIFT_MODULE_DEPENDS_WINDOWS}) @@ -1960,6 +1981,9 @@ function(add_swift_target_library name) set(swiftlib_c_compile_flags_all ${SWIFTLIB_C_COMPILE_FLAGS}) if(sdk IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) set(swiftlib_swift_compile_private_frameworks_flag "-Fsystem" "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/System/Library/PrivateFrameworks/") + foreach(tbd_lib ${SWIFTLIB_SWIFT_MODULE_DEPENDS_FROM_SDK}) + list(APPEND swiftlib_link_flags_all "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/usr/lib/swift/libswift${tbd_lib}.tbd") + endforeach() endif() list(APPEND swiftlib_c_compile_flags_all "-DSWIFT_TARGET_LIBRARY_NAME=${name}") diff --git a/cmake/modules/FindLibEdit.cmake b/cmake/modules/FindLibEdit.cmake new file mode 100644 index 0000000000000..b4f0cb3298114 --- /dev/null +++ b/cmake/modules/FindLibEdit.cmake @@ -0,0 +1,64 @@ +#.rst: +# FindLibEdit +# ----------- +# +# Find libedit library and headers +# +# The module defines the following variables: +# +# :: +# +# LibEdit_FOUND - true if libedit was found +# LibEdit_INCLUDE_DIRS - include search path +# LibEdit_LIBRARIES - libraries to link +# LibEdit_VERSION_STRING - version number + +if(LibEdit_INCLUDE_DIRS AND LibEdit_LIBRARIES) + set(LibEdit_FOUND TRUE) +else() + find_package(PkgConfig QUIET) + pkg_check_modules(PC_LIBEDIT QUIET libedit) + + find_path(LibEdit_INCLUDE_DIRS + NAMES + histedit.h + HINTS + ${PC_LIBEDIT_INCLUDEDIR} + ${PC_LIBEDIT_INCLUDE_DIRS} + ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + find_library(LibEdit_LIBRARIES + NAMES + edit libedit + HINTS + ${PC_LIBEDIT_LIBDIR} + ${PC_LIBEDIT_LIBRARY_DIRS} + ${CMAKE_INSTALL_FULL_LIBDIR}) + + if(LibEdit_INCLUDE_DIRS AND EXISTS "${LibEdit_INCLUDE_DIRS}/histedit.h") + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" + libedit_major_version_str + REGEX "^#define[ \t]+LIBEDIT_MAJOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MAJOR[ \t]+([0-9]+)" "\\1" + LIBEDIT_MAJOR_VERSION "${libedit_major_version_str}") + + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" + libedit_minor_version_str + REGEX "^#define[ \t]+LIBEDIT_MINOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MINOR[ \t]+([0-9]+)" "\\1" + LIBEDIT_MINOR_VERSION "${libedit_minor_version_str}") + + set(LibEdit_VERSION_STRING "${libedit_major_version}.${libedit_minor_version}") + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LibEdit + FOUND_VAR + LibEdit_FOUND + REQUIRED_VARS + LibEdit_INCLUDE_DIRS + LibEdit_LIBRARIES + VERSION_VAR + LibEdit_VERSION_STRING) + mark_as_advanced(LibEdit_INCLUDE_DIRS LibEdit_LIBRARIES) +endif() + diff --git a/cmake/modules/SwiftConfig.cmake.in b/cmake/modules/SwiftConfig.cmake.in index 7b0d002a75946..5dcdf5e1b9c1d 100644 --- a/cmake/modules/SwiftConfig.cmake.in +++ b/cmake/modules/SwiftConfig.cmake.in @@ -17,7 +17,7 @@ set(SWIFT_LIBRARY_DIR "@SWIFT_LIBRARY_DIRS@") set(SWIFT_CMAKE_DIR "@SWIFT_CMAKE_DIR@") set(SWIFT_BINARY_DIR "@SWIFT_BINARY_DIR@") -set(CMARK_TARGETS_FILE @SWIFT_PATH_TO_CMARK_BUILD@/src/CMarkExports.cmake) +set(CMARK_TARGETS_FILE @SWIFT_PATH_TO_CMARK_BUILD@/src/cmarkTargets.cmake) if(NOT TARGET libcmark_static AND EXISTS ${CMARK_TARGETS_FILE}) include(${CMARK_TARGETS_FILE}) endif() diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 3a87bfcd80bb2..f04e6c186f414 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -203,6 +203,8 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_${prefix}_ARCHITECTURES "${architectures}") if("${prefix}" STREQUAL "CYGWIN") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "COFF") + elseif("${prefix}" STREQUAL "WASI") + set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "WASM") else() set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "ELF") endif() @@ -319,6 +321,14 @@ macro(configure_sdk_unix name architectures) message(FATAL_ERROR "unsupported arch for Haiku: ${arch}") endif() set(SWIFT_SDK_HAIKU_ARCH_x86_64_TRIPLE "x86_64-unknown-haiku") + elseif("${prefix}" STREQUAL "WASI") + if(NOT arch STREQUAL wasm32) + message(FATAL_ERROR "unsupported arch for WebAssembly: ${arch}") + endif() + set(SWIFT_SDK_WASI_ARCH_wasm32_PATH "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot") + set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-wasi") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot/include") + set(SWIFT_SDK_WASI_ARCH_wasm32_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY "${SWIFT_WASI_SDK_PATH}/share/wasi-sysroot/include") else() message(FATAL_ERROR "unknown Unix OS: ${prefix}") endif() diff --git a/cmake/modules/SwiftSetIfArchBitness.cmake b/cmake/modules/SwiftSetIfArchBitness.cmake index 5212cf3ccb854..d38a9689150ba 100644 --- a/cmake/modules/SwiftSetIfArchBitness.cmake +++ b/cmake/modules/SwiftSetIfArchBitness.cmake @@ -12,7 +12,8 @@ function(set_if_arch_bitness var_name) "${SIA_ARCH}" STREQUAL "armv6" OR "${SIA_ARCH}" STREQUAL "armv7" OR "${SIA_ARCH}" STREQUAL "armv7k" OR - "${SIA_ARCH}" STREQUAL "armv7s") + "${SIA_ARCH}" STREQUAL "armv7s" OR + "${SIA_ARCH}" STREQUAL "wasm32") set("${var_name}" "${SIA_CASE_32_BIT}" PARENT_SCOPE) elseif("${SIA_ARCH}" STREQUAL "x86_64" OR "${SIA_ARCH}" STREQUAL "arm64" OR diff --git a/cmake/modules/SwiftSharedCMakeConfig.cmake b/cmake/modules/SwiftSharedCMakeConfig.cmake index 1a76d67adb786..5bddf37d86816 100644 --- a/cmake/modules/SwiftSharedCMakeConfig.cmake +++ b/cmake/modules/SwiftSharedCMakeConfig.cmake @@ -193,7 +193,7 @@ macro(swift_common_standalone_build_config_cmark product) include_directories("${CMARK_MAIN_INCLUDE_DIR}" "${CMARK_BUILD_INCLUDE_DIR}") - include(${PATH_TO_CMARK_BUILD}/src/CMarkExports.cmake) + include(${PATH_TO_CMARK_BUILD}/src/cmarkTargets.cmake) add_definitions(-DCMARK_STATIC_DEFINE) endmacro() diff --git a/docs/CompilerPerformance.md b/docs/CompilerPerformance.md index 97b23bb429b21..3f5599520711c 100644 --- a/docs/CompilerPerformance.md +++ b/docs/CompilerPerformance.md @@ -793,7 +793,7 @@ performance between two compilers, say `${OLD}/swiftc` and `${NEW}/swiftc`: ``` $ mkdir stats-old stats-new $ ${OLD}/swiftc -stats-output-dir stats-old test.swift -$ ${OLD}/swiftc -stats-output-dir stats-new test.swift +$ ${NEW}/swiftc -stats-output-dir stats-new test.swift $ utils/process-stats-dir.py --compare-stats-dirs stats-old stats-new old new delta_pct name 1402939 1430732 1.98 AST.NumASTBytesAllocated diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index d676f556d3c46..ce7adfa65e74f 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -2030,7 +2030,7 @@ and data structures that deal with differentiable function values, including: Arbitrary higher-order functions that require arguments to be differentiable functions. Differential operators, e.g. `derivative(of:)`, described in the -[differential operators and differentiation APIs](differential-operators-and-differentiation-apis) +[differential operators and differentiation APIs](#differential-operators-1) section. Differentiable higher-order functions for collections, e.g. [`Array.differentiableReduce(_:_:)`](https://gist.github.com/rxwei/2739515f77a62d26add66947cb179911). Data structures that store `@differentiable` functions as a property. Neural diff --git a/docs/GitWorkflows.rst b/docs/GitWorkflows.rst deleted file mode 100644 index 2027743712173..0000000000000 --- a/docs/GitWorkflows.rst +++ /dev/null @@ -1,281 +0,0 @@ -:orphan: - -.. highlight:: bash - -Git Workflows -============= - -Purpose -------- - -Swift development has been based on SVN since its inception. As part of the -transition to Git this document helps to address questions about how common SVN -workflows we use today translate to their Git counterparts as well as to discuss -Git workflow practices we plan on having -- at least initially -- after the Git -transition. Notably we will follow a model where commits to trunk -- which is -the 'master' branch in Git -- has commits land (in the common case) via rebasing -instead of merging. This model is open to evolution later, but this mimics the -workflow we have today with SVN. - -SVN -> GIT Workflows -==================== - -The general SVN workflow consists of the following commands: - -1. Checkout: This means checking out/setting up a new repository. -2. Update: Pulling the latest remote changes into a local repository. -3. Committing: Committing a change to the remote repository. -4. Reverting: Reverting a change from a remote repository. -5. Browsing: Looking at commits. - -This document will show how to translate these commands to Git and additionally -how to configure Git. It assumes that one is attempting to manipulate a Git -repository via bash in a terminal. A lot of information since this is supposed -to be a short, actionable guide. For more information, please see the Git crash -course guide for SVN users at - -*NOTE* Whenever we say the Swift repository, we mean any repository in the -Swift project. - -Quicksetup (TLDR) ------------------ - -For those who do not want to read the full document, use the following commands -to perform a simple repo setup for the Swift repository:: - - $ git config --global user.name "" - $ git config --global user.email "" - $ mkdir swift-source && cd swift-source - $ git clone - $ git clone - $ git clone - $ (cd swift && git config branch.autosetuprebase always) - $ git clone - $ git clone - -Then to commit a new commit to the remote swift repository:: - - $ git commit - $ git push origin master - -and to pull new commits from the remote swift repository:: - - $ git pull origin master - -In order to ease updating all repositories, consider using the script in -'./utils/update-checkout'. This will automate updating the repositories in the -proper way. - -Preliminary ------------ - -Before beginning, we need to perform some global configuration of Git. Git -includes a username/email of the committer in every commit. By default this is -the current logged in user and the hostname of the machine. This is /not/ what -one wants. We configure Git globally (i.e. across all repositories) to have our -proper name and email by running the following commands:: - - $ git config --global user.name "" - $ git config --global user.email "" - -Checkout --------- - -Normally if one wishes to checkout a repository in SVN, one would use a command -like this:: - - $ SVN co - -This would then checkout the latest revision from the repository specified by -'repository url' and place it into the directory 'local directory'. In Git, -instead of checking out the repository, one clones the repository. This is done -as follows:: - - $ git clone - -This will cause Git to clone the repository at 'repository url' and check out -the 'master' branch. The 'master' branch corresponds to 'trunk' in SVN. For more -information about branching in Git please see - - -Before beginning to commit though, we /must/ perform some default configuration -of our repository to match the Swift repository default configuration by -enabling default rebasing. - -Repository Configuration (Enabling Default Rebasing) ----------------------------------------------------- - -Once we have cloned the repository, we need to configure the repository for our -use. Specifically we want to configure the swift repository so that we rebase -whenever we update the repository (see the update section below for more -details):: - - $ git config branch.autosetuprebase always - -By default when updating, Git will attempt to merge the remote changes and your -local changes. Ignoring what that sentence means, this is not an SVN-esque -model. Instead we want any local changes that we have to be applied on top of -any new remote changes. The 'branch.autosetuprebase' flag causes this to be done -automatically whenever one updates the local repository. - -Update ------- - -In SVN, one updates your local repository by running the update command:: - - $ SVN update - -In Git, instead of performing SVN update, one pulls from the remote repository:: - - $ git pull --rebase origin master - -This will pull any new remote commits into your local repository and then replay -your current local commits on top of those new commits. - -By default the '--rebase' flag is not necessary for the Swift repository because -it is configured to always rebase by setting the 'branch.autosetuprebase' flag -(see the section 'Repository Configuration (Enabling Default Rebasing)' above). - -Commit ------- - -In SVN, committing always means submitting changes to a remote repository. In -Git, committing refers to the process of first telling Git to track a change by -staging the change and then committing all staged changes into a change in the -local repository. One can have many such commits. Then when one is ready, one -pushes the new local changes to the remote repository. We go through these steps -in more detail below: - -In terms of replicating the SVN model, there are now two steps. In order to -commit changes one first stages a changed file using 'git add':: - - $ git add - -Then once all changes that you want to be apart of the commit have been staged, -a commit is created in the local repository via the 'commit' command:: - - $ git commit - -As a shortcut to commit /all/ changes to local files that are already being -tracked by Git to the local repository, you can use the '-a' command:: - - $ git commit -a - -In both of these cases, an editor will pop up to accept a commit message. To -specify a short commit message at the commandline, you can use the '-m' flag:: - - $ git commit -m 'My great commit message.' - -In order to see the diff of changes that have not been staged, run the command:: - - $ git diff - -To see all changes that have been staged, run the command:: - - $ git diff --staged - -To get a diff for a specific revision/path, perform the following command:: - - $ git diff - -In order to get a more concise view of the files that have staged and or -unstaged changes, run the command:: - - $ git status - -In order to restore a file from the last revision, one uses the checkout -command:: - - $ git checkout - -To restore a file to a specific revision, one must use a longer form of the -command:: - - $ git checkout -- - -To unstage a file, one uses the 'reset' command:: - - $ git reset - -This tells Git to reset '' in the staging area to the top of tree commit -(which in Git is called 'HEAD'). In order to correct a mistake, you can pass the -'amend' flag to Git:: - - $ git commit --amend - -This will cause all staged changes to be merged into 'HEAD'. Once one has made -all the relevant commits, in order to push the changes to the remote repository -the 'push' command is used:: - - $ git push origin master - -If a different committer has committed changes such that there are remote -commits that are not present locally, this will fail. In order to get around -this issue, perform:: - - $ git pull --rebase origin master - -in order to pull the new remote commits and replay your new commits on top. Then -try to push again. See the 'Checkout' section above how to configure the local -swift repository to always rebase allowing you to drop the '--rebase' flag. - -Revert ------- - -In SVN reverting a commit implies performing a reverse merge. In Git, this is no -longer true. Instead one now just uses the 'revert' command:: - - $ git revert - -This will cause Git to perform the reverse merge of that revision for you -against HEAD and bring up a message window for you to write a commit -message. This will be autofilled in with the title of the commit that is going -to be reverted and the revision number of that commit like so:: - - Revert "" - - This reverts commit . - -One can edit this message as one sees fit. Once this has been done, the revert -will become a normal commit in your repository like any other commit. Thus to -revert the commit in the remote repository, you need to perform a Git push:: - - $ git push origin master - -Browsing --------- - -This section explains how one can view Git changes. In order to view a history -of all changes on a branch to the beginning of time use the 'log' command:: - - $ git log - -This will for each commit show the following information:: - - commit - Author: - Date: - - - -To see history starting at a specific commit use the following form of a Git log -command:: - - $ git log - -To see an oneline summary that includes just the title of the commit and its -hash, pass the '--oneline' command:: - - $ git log --oneline - -It will not show you what was actually changed in each commit. In order to see -what was actually changed in a commit, use the command 'show':: - - $ git show - -This will show the aforementioned information shown by Git log, but additionally -will perform a diff against top of tree showing you the contents of the -change. To see the changes for a specific commit, pass the revision to Git -show:: - - $ git show diff --git a/docs/OptimizationTips.rst b/docs/OptimizationTips.rst index 60171e08f3a13..18ed2644e6707 100644 --- a/docs/OptimizationTips.rst +++ b/docs/OptimizationTips.rst @@ -319,7 +319,7 @@ generics. Some more examples of generics: func pop() -> T { ... } } - func myAlgorithm(_ a: [T], length: Int) { ... } + func myAlgorithm(_ a: [T], length: Int) { ... } // The compiler can specialize code of MyStack var stackOfInts: MyStack @@ -446,7 +446,7 @@ construct such a data structure: var value: T { get { return ref.val } set { - if (!isKnownUniquelyReferenced(&ref)) { + if !isKnownUniquelyReferenced(&ref) { ref = Ref(newValue) return } diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.rst index dee4f32198764..ac856e2481354 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.rst @@ -370,12 +370,12 @@ guesswork. However, we note that the type of an enum member actually has a regular structure. For example, consider the ``Optional`` type:: enum Optional { - case None - case Some(T) + case none + case some(T) } -The type of ``Optional.None`` is ``Optional``, while the type of -``Optional.Some`` is ``(T) -> Optional``. In fact, the +The type of ``Optional.none`` is ``Optional``, while the type of +``Optional.some`` is ``(T) -> Optional``. In fact, the type of an enum element can have one of two forms: it can be ``T0``, for an enum element that has no extra data, or it can be ``T2 -> T0``, where ``T2`` is the data associated with the enum element. For the @@ -985,9 +985,6 @@ The things in the queue yet to be ported are: Most of the associated diagnostics have been ported and fixes are located in ``ConstraintSystem::simplifyMemberConstraint``. -- Diagnostics related to ``if`` statement - "conditional" type mismatch - and, in case of ternary operator, type mismatches between branches. - - Problems related to calls and operator applications e.g. - Missing explicit ``Self.`` and ``self.`` diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index ab8c0afa3af0e..0fb39e7d87c07 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -1,19 +1,25 @@ # Building Swift on Windows -Visual Studio 2017 or newer is needed to build swift on Windows. The following must take place in the developer command prompt (provided by Visual Studio). This shows up as "x64 Native Tools Command Prompt for VS2017" (or VS2019, VS2019 Preview depending on the Visual Studio that you are using) in the Start Menu. +Visual Studio 2017 or newer is needed to build swift on Windows. + +The following must take place in the **developer command prompt** (provided by Visual Studio). This shows up as "x64 Native Tools Command Prompt for VS2017" (or VS2019, VS2019 Preview depending on the Visual Studio that you are using) in the Start Menu. ## Install dependencies + - Install the latest version of [Visual Studio](https://www.visualstudio.com/downloads/) -- Make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The following components are required: +- Make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The following components are required: 1. Microsoft.VisualStudio.Component.Windows10SDK -1. Microsoft.VisualStudio.Component.Windows10SDK.17763 -1. Microsoft.VisualStudio.Component.VC.Tools.x86.x64 +2. Microsoft.VisualStudio.Component.Windows10SDK.17763 +3. Microsoft.VisualStudio.Component.VC.Tools.x86.x64 + +The following [link](https://docs.microsoft.com/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2019)) helps in finding the component name given its ID for Visual Studio 2019. ## Clone the repositories + 1. Clone `apple/llvm-project` into a directory for the toolchain -1. Clone `apple/swift-cmark`, `apple/swift`, `apple/swift-corelibs-libdispatch`, `apple/swift-corelibs-foundation`, `apple/swift-corelibs-xctest`, `apple/swift-llbuild`, `apple/swift-package-manager` into the toolchain directory -1. Clone `compnerd/windows-swift` as a peer of the toolchain directory +2. Clone `apple/swift-cmark`, `apple/swift`, `apple/swift-corelibs-libdispatch`, `apple/swift-corelibs-foundation`, `apple/swift-corelibs-xctest`, `apple/swift-llbuild`, `apple/swift-package-manager` into the toolchain directory +3. Clone `compnerd/windows-swift` as a peer of the toolchain directory - Currently, other repositories in the Swift project have not been tested and may not be supported. @@ -37,8 +43,17 @@ git clone -c core.autocrlf=input https://github.com/apple/swift-package-manager git clone https://github.com/compnerd/windows-swift windows-swift ``` -## Acquire ICU, SQLite3, curl, libxml2, zlib -1. Go to https://dev.azure.com/compnerd/windows-swift and scroll down to "Dependencies" where you'll see bots (hopefully green) for icu, SQLite, curl, and libxml2. Download each of the zip files and copy their contents into S:/Library. The directory structure should resemble: +## Acquire ICU, SQLite3, curl, libxml2 and zlib + +Go to [compnerd's windows-swift azure page](https://dev.azure.com/compnerd/windows-swift/_build) and open [Pipelines](https://dev.azure.com/compnerd/windows-swift/_build) where you'll see bots (hopefully green) for: + +- [ICU](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=9) +- [SQLite](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=12&_a=summary) +- [curl](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=11&_a=summary) +- [libxml2](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=10&_a=summary) +- [zlib](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=16&_a=summary) + +Download each of the zip files and copy their contents into S:/Library. The directory structure should resemble: ``` /Library @@ -55,9 +70,14 @@ git clone https://github.com/compnerd/windows-swift windows-swift ``` ## One-time Setup (re-run on Visual Studio upgrades) -- Set up the `ucrt`, `visualc`, and `WinSDK` modules by copying `ucrt.modulemap` located at - `swift/stdlib/public/Platform/ucrt.modulemap` into - `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap`, copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap`, and copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` + +Set up the `ucrt`, `visualc`, and `WinSDK` modules by: + +- copying `ucrt.modulemap` located at `swift/stdlib/public/Platform/ucrt.modulemap` into + `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap` +- copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap` +- copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` +- and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` ```cmd mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" S:\toolchain\swift\stdlib\public\Platform\ucrt.modulemap @@ -104,6 +124,7 @@ ninja -C S:\b\foundation ``` - Add Foundation to your path: + ```cmd path S:\b\foundation\Foundation;%PATH% ``` @@ -116,6 +137,7 @@ ninja -C S:\b\xctest ``` - Add XCTest to your path: + ```cmd path S:\b\xctest;%PATH% ``` @@ -137,7 +159,7 @@ ninja -C S:\b\foundation ```cmd cmake --build S:\b\foundation -ninja -C S:\b\foundation test +ninja -C S:\b\foundation test ``` ## Build llbuild @@ -148,7 +170,8 @@ cmake -B S:\b\llbuild -G Ninja -S S:\toolchain\llbuild -DCMAKE_BUILD_TYPE=RelWit ninja -C S:\b\llbuild ``` - - Add llbuild to your path: +- Add llbuild to your path: + ```cmd path S:\b\llbuild\bin;%PATH% ``` @@ -171,8 +194,8 @@ ninja -C S:\b\spm - Run ninja install: -```cmd +```cmd ninja -C S:\b\toolchain install ``` -- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. +- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. diff --git a/docs/tools/swift.pod b/docs/tools/swift.pod index 1989ed851b255..ead511b2ad0de 100644 --- a/docs/tools/swift.pod +++ b/docs/tools/swift.pod @@ -86,7 +86,7 @@ for Swift, an open-source project, is located at L. If a bug can be reproduced only within an Xcode project or a playground, or if the bug is associated with an Apple NDA, please file a report to Apple's -bug reporter at L instead. +Feedback Assistant at L instead. =head1 SEE ALSO diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 34b0c0f1affc8..951abc2cc3070 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -160,6 +160,7 @@ using TargetRelativeIndirectablePointer struct HeapObject; class WeakReference; +struct UnownedReference; template struct TargetMetadata; using Metadata = TargetMetadata; @@ -645,6 +646,9 @@ struct TargetMetadata { // NOTE: This *is* a box for copy-on-write existentials. OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const; + // Deallocate an out-of-line buffer box if one is present. + void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const; + /// Get the nominal type descriptor if this metadata describes a nominal type, /// or return null if it does not. ConstTargetMetadataPointer @@ -693,6 +697,8 @@ struct TargetMetadata { bool satisfiesClassConstraint() const; + bool isCanonicalStaticallySpecializedGenericMetadata() const; + #if SWIFT_OBJC_INTEROP /// Get the ObjC class object for this type if it has one, or return null if /// the type is not a class (or not a class with a class object). @@ -1344,6 +1350,34 @@ struct TargetStructMetadata : public TargetValueMetadata { return reinterpret_cast(asWords + offset); } + bool isCanonicalStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isCanonicalStaticSpecialization(); + } + + const MetadataTrailingFlags *getTrailingFlags() const { + auto description = getDescription(); + auto flags = description->getFullGenericContextHeader() + .DefaultInstantiationPattern->PatternFlags; + if (!flags.hasTrailingFlags()) + return nullptr; + auto fieldOffset = description->FieldOffsetVectorOffset; + auto offset = + fieldOffset + + // Pad to the nearest pointer. + ((description->NumFields * sizeof(uint32_t) + sizeof(void *) - 1) / + sizeof(void *)); + auto asWords = reinterpret_cast(this); + return reinterpret_cast(asWords + offset); + } + static constexpr int32_t getGenericArgumentOffset() { return sizeof(TargetStructMetadata) / sizeof(StoredPointer); } @@ -2827,8 +2861,8 @@ struct TargetExtensionContextDescriptor final TrailingGenericContextObjects> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects>; public: /// A mangling of the `Self` type context that the extension extends. @@ -2868,10 +2902,10 @@ struct TargetAnonymousContextDescriptor final TargetMangledContextName> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - TargetMangledContextName>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + TargetMangledContextName>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3033,10 +3067,10 @@ struct TargetOpaqueTypeDescriptor final RelativeDirectPointer> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - RelativeDirectPointer>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + RelativeDirectPointer>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3571,7 +3605,7 @@ struct TargetSingletonMetadataInitialization { } /// This method can only be called from the runtime itself. It is defined - /// in MetadataCache.h. + /// in Metadata.cpp. TargetMetadata *allocate( const TargetTypeContextDescriptor *description) const; }; @@ -3799,16 +3833,16 @@ class TargetClassDescriptor final TargetObjCResilientClassStubInfo> { private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - TargetResilientSuperclass, - TargetForeignMetadataInitialization, - TargetSingletonMetadataInitialization, - TargetVTableDescriptorHeader, - TargetMethodDescriptor, - TargetOverrideTableHeader, - TargetMethodOverrideDescriptor, - TargetObjCResilientClassStubInfo>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + TargetResilientSuperclass, + TargetForeignMetadataInitialization, + TargetSingletonMetadataInitialization, + TargetVTableDescriptorHeader, + TargetMethodDescriptor, + TargetOverrideTableHeader, + TargetMethodOverrideDescriptor, + TargetObjCResilientClassStubInfo>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4116,10 +4150,10 @@ class TargetStructDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4192,10 +4226,10 @@ class TargetEnumDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index cc15eed09bb8c..6077c0019ed4b 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -913,55 +913,6 @@ class TargetTupleTypeFlags { }; using TupleTypeFlags = TargetTupleTypeFlags; -/// Field types and flags as represented in a nominal type's field/case type -/// vector. -class FieldType { - typedef uintptr_t int_type; - // Type metadata is always at least pointer-aligned, so we get at least two - // low bits to stash flags. We could use three low bits on 64-bit, and maybe - // some high bits as well. - enum : int_type { - Indirect = 1, - Weak = 2, - - TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1), - }; - int_type Data; - - constexpr FieldType(int_type Data) : Data(Data) {} -public: - constexpr FieldType() : Data(0) {} - FieldType withType(const Metadata *T) const { - return FieldType((Data & ~TypeMask) | (uintptr_t)T); - } - - constexpr FieldType withIndirect(bool indirect) const { - return FieldType((Data & ~Indirect) - | (indirect ? Indirect : 0)); - } - - constexpr FieldType withWeak(bool weak) const { - return FieldType((Data & ~Weak) - | (weak ? Weak : 0)); - } - - bool isIndirect() const { - return bool(Data & Indirect); - } - - bool isWeak() const { - return bool(Data & Weak); - } - - const Metadata *getType() const { - return (const Metadata *)(Data & TypeMask); - } - - int_type getIntValue() const { - return Data; - } -}; - /// Flags for exclusivity-checking operations. enum class ExclusivityFlags : uintptr_t { Read = 0x0, @@ -1560,6 +1511,10 @@ class GenericMetadataPatternFlags : public FlagSet { /// Does this pattern have an extra-data pattern? HasExtraDataPattern = 0, + /// Do instances of this pattern have a bitset of flags that occur at the + /// end of the metadata, after the extra data if there is any? + HasTrailingFlags = 1, + // Class-specific flags. /// Does this pattern have an immediate-members pattern? @@ -1584,6 +1539,10 @@ class GenericMetadataPatternFlags : public FlagSet { hasExtraDataPattern, setHasExtraDataPattern) + FLAGSET_DEFINE_FLAG_ACCESSORS(HasTrailingFlags, + hasTrailingFlags, + setHasTrailingFlags) + FLAGSET_DEFINE_FIELD_ACCESSORS(Value_MetadataKind, Value_MetadataKind_width, MetadataKind, @@ -1716,6 +1675,30 @@ class MetadataRequest : public FlagSet { } }; +struct MetadataTrailingFlags : public FlagSet { + enum { + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation. + IsStaticSpecialization = 0, + + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation and made to be canonical by + /// modifying the metadata accessor. + IsCanonicalStaticSpecialization = 1, + }; + + explicit MetadataTrailingFlags(uint64_t bits) : FlagSet(bits) {} + constexpr MetadataTrailingFlags() {} + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsStaticSpecialization, + isStaticSpecialization, + setIsStaticSpecialization) + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsCanonicalStaticSpecialization, + isCanonicalStaticSpecialization, + setIsCanonicalStaticSpecialization) +}; + /// Flags for Builtin.IntegerLiteral values. class IntegerLiteralFlags { public: diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index d54fd98c1ac0c..53bf0aafb0d21 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -25,6 +25,7 @@ #include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/LangOptions.h" +#include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -290,7 +291,7 @@ class ASTContext final { // derivative function configurations per original `AbstractFunctionDecl`. llvm::DenseMap< std::tuple, - DerivativeAttr *> + llvm::SmallPtrSet> DerivativeAttrs; private: @@ -489,6 +490,9 @@ class ASTContext final { /// Get the '+' function on two String. FuncDecl *getPlusFunctionOnString() const; + /// Get Sequence.makeIterator(). + FuncDecl *getSequenceMakeIterator() const; + /// Check whether the standard library provides all the correct /// intrinsic support for Optional. /// @@ -610,6 +614,14 @@ class ASTContext final { /// swift_getTypeByMangledNameInContextInMetadataState. AvailabilityContext getTypesInAbstractMetadataStateAvailability(); + /// Get the runtime availability of support for prespecialized generic + /// metadata. + AvailabilityContext getPrespecializedGenericMetadataAvailability(); + + /// Get the runtime availability of features introduced in the Swift 5.2 + /// compiler for the target platform. + AvailabilityContext getSwift52Availability(); + //===--------------------------------------------------------------------===// // Diagnostics Helper functions @@ -718,12 +730,12 @@ class ASTContext final { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - bool canImportModule(std::pair ModulePath); + bool canImportModule(Located ModulePath); /// \returns a module with a given name that was already loaded. If the /// module was not loaded, returns nullptr. ModuleDecl *getLoadedModule( - ArrayRef> ModulePath) const; + ArrayRef> ModulePath) const; ModuleDecl *getLoadedModule(Identifier ModuleName) const; @@ -733,7 +745,7 @@ class ASTContext final { /// be returned. /// /// \returns The requested module, or NULL if the module cannot be found. - ModuleDecl *getModule(ArrayRef> ModulePath); + ModuleDecl *getModule(ArrayRef> ModulePath); ModuleDecl *getModuleByName(StringRef ModuleName); diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index a9e9b89a99005..98d665b1fd728 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -14,6 +14,7 @@ #define SWIFT_AST_ASTPRINTER_H #include "swift/Basic/LLVM.h" +#include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" #include "llvm/ADT/StringRef.h" @@ -185,6 +186,8 @@ class ASTPrinter { return *this; } + ASTPrinter &operator<<(QuotedString s); + ASTPrinter &operator<<(unsigned long long N); ASTPrinter &operator<<(UUID UU); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 02db70aba959b..abf03f84eb2e0 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -479,6 +479,10 @@ class ASTScopeImpl { /// asked. virtual Optional> computeSelfDCForParent() const; + /// Returns the context that should be used when a nested scope (e.g. a + /// closure) captures self explicitly. + virtual NullablePtr capturedSelfDC() const; + protected: /// Find either locals or members (no scope has both) /// \param history The scopes visited since the start of lookup (including @@ -692,6 +696,15 @@ class Portion { /// to compute the selfDC from the history. static NullablePtr computeSelfDC(ArrayRef history); + + /// If we find a lookup result that requires the dynamic implict self value, + /// we need to check the nested scopes to see if any closures explicitly + /// captured \c self. In that case, the appropriate selfDC is that of the + /// innermost closure which captures a \c self value from one of this type's + /// methods. + static NullablePtr + checkNestedScopesForSelfCapture(ArrayRef history, + size_t start); }; /// Behavior specific to representing the trailing where clause of a @@ -1449,6 +1462,13 @@ class ClosureParametersScope final : public AbstractClosureScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + + /// Since explicit captures of \c self by closures enable the use of implicit + /// \c self, we need to make sure that the appropriate \c self is used as the + /// base decl for these uses (otherwise, the capture would be marked as + /// unused. \c ClosureParametersScope::capturedSelfDC() checks if we have such + /// a capture of self. + NullablePtr capturedSelfDC() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 5c52e3bd053bb..bc57d7ae7ba8b 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -208,6 +208,23 @@ class AnyFunctionRef { llvm_unreachable("unexpected AnyFunctionRef representation"); } + friend bool operator==(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction == rhs.TheFunction; + } + + friend bool operator!=(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction != rhs.TheFunction; + } + + friend llvm::hash_code hash_value(AnyFunctionRef fn) { + using llvm::hash_value; + return hash_value(fn.TheFunction.getOpaqueValue()); + } + + friend SourceLoc extractNearestSourceLoc(AnyFunctionRef fn) { + return fn.getLoc(); + } + private: ArrayRef getYieldResultsImpl(SmallVectorImpl &buffer, @@ -235,6 +252,8 @@ class AnyFunctionRef { #pragma warning(pop) #endif +void simple_display(llvm::raw_ostream &out, AnyFunctionRef fn); + } // namespace swift namespace llvm { diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index dd328b9962b56..e9b72bb7c55b6 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -52,6 +52,7 @@ TYPE_ATTR(convention) TYPE_ATTR(noescape) TYPE_ATTR(escaping) TYPE_ATTR(differentiable) +TYPE_ATTR(noDerivative) // SIL-specific attributes TYPE_ATTR(block_storage) @@ -540,6 +541,11 @@ DECL_ATTR(_implicitly_synthesizes_nested_requirement, ImplicitlySynthesizesNeste ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 98) +DECL_ATTR(transpose, Transpose, + OnFunc | LongAttribute | AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 99) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 896396d8c78cd..b42fdaf4da5a1 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -25,6 +25,7 @@ #include "swift/Basic/Range.h" #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Version.h" +#include "swift/Basic/Located.h" #include "swift/AST/Identifier.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AutoDiff.h" @@ -69,16 +70,13 @@ class TypeAttributes { struct Convention { StringRef Name = {}; DeclNameRef WitnessMethodProtocol = {}; - StringRef ClangType = {}; - // Carry the source location for diagnostics. - SourceLoc ClangTypeLoc = {}; - + Located ClangType = Located(StringRef(), {}); /// Convenience factory function to create a Swift convention. /// /// Don't use this function if you are creating a C convention as you /// probably need a ClangType field as well. static Convention makeSwiftConvention(StringRef name) { - return {name, DeclNameRef(), "", {}}; + return {name, DeclNameRef(), Located("", {})}; } }; @@ -1625,6 +1623,8 @@ class OriginallyDefinedInAttr: public DeclAttribute { /// Indicates when the symbol was moved here. const llvm::VersionTuple MovedVersion; + /// Returns true if this attribute is active given the current platform. + bool isActivePlatform(const ASTContext &ctx) const; static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_OriginallyDefinedIn; } @@ -1648,9 +1648,13 @@ class DifferentiableAttr final ParsedAutoDiffParameter> { friend TrailingObjects; + /// The declaration on which the `@differentiable` attribute is declared. + /// May not be a valid declaration for `@differentiable` attributes. + /// Resolved during parsing and deserialization. + Decl *OriginalDeclaration = nullptr; /// Whether this function is linear (optional). bool Linear; - /// The number of parsed parameters specified in 'wrt:'. + /// The number of parsed differentiability parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; /// The JVP function. Optional JVP; @@ -1662,7 +1666,7 @@ class DifferentiableAttr final /// The VJP function (optional), resolved by the type checker if VJP name is /// specified. FuncDecl *VJPFunction = nullptr; - /// The differentiation parameters' indices, resolved by the type checker. + /// The differentiability parameter indices, resolved by the type checker. IndexSubset *ParameterIndices = nullptr; /// The trailing where clause (optional). TrailingWhereClause *WhereClause = nullptr; @@ -1703,6 +1707,12 @@ class DifferentiableAttr final Optional vjp, GenericSignature derivativeGenSig); + Decl *getOriginalDeclaration() const { return OriginalDeclaration; } + + /// Sets the original declaration on which this attribute is declared. + /// Should only be used by parsing and deserialization. + void setOriginalDeclaration(Decl *originalDeclaration); + /// Get the optional 'jvp:' function name and location. /// Use this instead of `getJVPFunction` to check whether the attribute has a /// registered JVP. @@ -1720,7 +1730,7 @@ class DifferentiableAttr final ParameterIndices = parameterIndices; } - /// The parsed differentiation parameters, i.e. the list of parameters + /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { return {getTrailingObjects(), NumParsedParameters}; @@ -1755,31 +1765,30 @@ class DifferentiableAttr final // Print the attribute to the given stream. // If `omitWrtClause` is true, omit printing the `wrt:` clause. - // If `omitAssociatedFunctions` is true, omit printing associated functions. - void print(llvm::raw_ostream &OS, const Decl *D, - bool omitWrtClause = false, - bool omitAssociatedFunctions = false) const; + // If `omitDerivativeFunctions` is true, omit printing derivative functions. + void print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause = false, + bool omitDerivativeFunctions = false) const; static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Differentiable; } }; -/// The `@derivative` attribute registers a function as a derivative of another -/// function-like declaration: a 'func', 'init', 'subscript', or 'var' computed -/// property declaration. +/// The `@derivative(of:)` attribute registers a function as a derivative of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. /// -/// The `@derivative` attribute also has an optional `wrt:` clause specifying -/// the parameters that are differentiated "with respect to", i.e. the -/// differentiation parameters. The differentiation parameters must conform to -/// the `Differentiable` protocol. +/// The `@derivative(of:)` attribute also has an optional `wrt:` clause +/// specifying the parameters that are differentiated "with respect to", i.e. +/// the differentiability parameters. The differentiability parameters must +/// conform to the `Differentiable` protocol. /// -/// If the `wrt:` clause is unspecified, the differentiation parameters are +/// If the `wrt:` clause is unspecified, the differentiability parameters are /// inferred to be all parameters that conform to `Differentiable`. /// -/// `@derivative` attribute type-checking verifies that the type of the +/// `@derivative(of:)` attribute type-checking verifies that the type of the /// derivative function declaration is consistent with the type of the -/// referenced original declaration and the differentiation parameters. +/// referenced original declaration and the differentiability parameters. /// /// Examples: /// @derivative(of: sin(_:)) @@ -1789,35 +1798,44 @@ class DerivativeAttr final private llvm::TrailingObjects { friend TrailingObjects; + /// The base type for the referenced original declaration. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. + TypeRepr *BaseTypeRepr; /// The original function name. DeclNameRefWithLoc OriginalFunctionName; /// The original function declaration, resolved by the type checker. AbstractFunctionDecl *OriginalFunction = nullptr; - /// The number of parsed parameters specified in 'wrt:'. + /// The number of parsed differentiability parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; - /// The differentiation parameters' indices, resolved by the type checker. + /// The differentiability parameter indices, resolved by the type checker. IndexSubset *ParameterIndices = nullptr; /// The derivative function kind (JVP or VJP), resolved by the type checker. Optional Kind = None; explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, - DeclNameRefWithLoc original, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, ArrayRef params); explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, - DeclNameRefWithLoc original, IndexSubset *indices); + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); public: static DerivativeAttr *create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, ArrayRef params); static DerivativeAttr *create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, - IndexSubset *indices); + IndexSubset *parameterIndices); + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } DeclNameRefWithLoc getOriginalFunctionName() const { return OriginalFunctionName; } @@ -1834,7 +1852,7 @@ class DerivativeAttr final } void setDerivativeKind(AutoDiffDerivativeFunctionKind kind) { Kind = kind; } - /// The parsed differentiation parameters, i.e. the list of parameters + /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { return {getTrailingObjects(), NumParsedParameters}; @@ -1858,6 +1876,90 @@ class DerivativeAttr final } }; +/// The `@transpose(of:)` attribute registers a function as a transpose of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. +/// +/// The `@transpose(of:)` attribute also has a `wrt:` clause specifying the +/// parameters that are transposed "with respect to", i.e. the linearity +/// parameters. +/// +/// Examples: +/// @transpose(of: foo) +/// @transpose(of: +, wrt: (0, 1)) +class TransposeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The base type for the referenced original declaration. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. + TypeRepr *BaseTypeRepr; + /// The original function name. + DeclNameRefWithLoc OriginalFunctionName; + /// The original function declaration, resolved by the type checker. + AbstractFunctionDecl *OriginalFunction = nullptr; + /// The number of parsed linearity parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The linearity parameter indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + +public: + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } + DeclNameRefWithLoc getOriginalFunctionName() const { + return OriginalFunctionName; + } + AbstractFunctionDecl *getOriginalFunction() const { + return OriginalFunction; + } + void setOriginalFunction(AbstractFunctionDecl *decl) { + OriginalFunction = decl; + } + + /// The parsed linearity parameters, i.e. the list of parameters specified in + /// 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *parameterIndices) { + ParameterIndices = parameterIndices; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Transpose; + } +}; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index fc2059f907eed..cc0f8ec97b105 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -19,6 +19,7 @@ #include +#include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/IndexSubset.h" #include "swift/AST/Type.h" @@ -70,6 +71,25 @@ struct AutoDiffDerivativeFunctionKind { } }; +/// Identifies an autodiff derivative function configuration: +/// - Parameter indices. +/// - Result indices. +/// - Derivative generic signature (optional). +struct AutoDiffConfig { + IndexSubset *parameterIndices; + IndexSubset *resultIndices; + GenericSignature derivativeGenericSignature; + + /*implicit*/ AutoDiffConfig(IndexSubset *parameterIndices, + IndexSubset *resultIndices, + GenericSignature derivativeGenericSignature) + : parameterIndices(parameterIndices), resultIndices(resultIndices), + derivativeGenericSignature(derivativeGenericSignature) {} + + void print(llvm::raw_ostream &s = llvm::outs()) const; + SWIFT_DEBUG_DUMP; +}; + class ParsedAutoDiffParameter { public: enum class Kind { Named, Ordered, Self }; @@ -148,10 +168,59 @@ void getSubsetParameterTypes(IndexSubset *indices, AnyFunctionType *type, namespace llvm { +using swift::AutoDiffConfig; using swift::AutoDiffDerivativeFunctionKind; +using swift::GenericSignature; +using swift::IndexSubset; template struct DenseMapInfo; +template <> struct DenseMapInfo { + static AutoDiffConfig getEmptyKey() { + auto *ptr = llvm::DenseMapInfo::getEmptyKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static AutoDiffConfig getTombstoneKey() { + auto *ptr = llvm::DenseMapInfo::getTombstoneKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static unsigned getHashValue(const AutoDiffConfig &Val) { + auto canGenSig = + Val.derivativeGenericSignature + ? Val.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + unsigned combinedHash = hash_combine( + ~1U, DenseMapInfo::getHashValue(Val.parameterIndices), + DenseMapInfo::getHashValue(Val.resultIndices), + DenseMapInfo::getHashValue(canGenSig)); + return combinedHash; + } + + static bool isEqual(const AutoDiffConfig &LHS, const AutoDiffConfig &RHS) { + auto lhsCanGenSig = + LHS.derivativeGenericSignature + ? LHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + auto rhsCanGenSig = + RHS.derivativeGenericSignature + ? RHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + return LHS.parameterIndices == RHS.parameterIndices && + LHS.resultIndices == RHS.resultIndices && + DenseMapInfo::isEqual(lhsCanGenSig, rhsCanGenSig); + } +}; + template <> struct DenseMapInfo { static AutoDiffDerivativeFunctionKind getEmptyKey() { return static_cast( diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 03809bcef7008..4b5cd1888a30a 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -641,8 +641,10 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special) /// globalStringTablePointer has type String -> Builtin.RawPointer. /// It returns an immortal, global string table pointer for strings constructed -/// from string literals. -BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "", Special) +/// from string literals. We consider it effects as readnone meaning that it +/// does not read any memory (note that even though it reads from a string, it +/// is a pure value and therefore we can consider it as readnone). +BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "n", Special) #undef BUILTIN_MISC_OPERATION_WITH_SILGEN diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index a3d53be0584a7..0e3d109226fd0 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -21,6 +21,7 @@ class CompilerInstance; class Preprocessor; class Sema; class TargetInfo; +class Type; } // namespace clang namespace swift { @@ -100,6 +101,16 @@ class ClangModuleLoader : public ModuleLoader { lookupRelatedEntity(StringRef clangName, ClangTypeKind kind, StringRef relatedEntityKind, llvm::function_ref receiver) = 0; + + /// Try to parse the string as a Clang function type. + /// + /// Returns null if there was a parsing failure. + virtual const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const = 0; + + /// Print the Clang type. + virtual void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const = 0; }; } // namespace swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 527f684081ca8..9720bf10af2ac 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -40,6 +40,7 @@ #include "swift/Basic/NullablePtr.h" #include "swift/Basic/OptionalEnum.h" #include "swift/Basic/Range.h" +#include "swift/Basic/Located.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/TrailingObjects.h" @@ -334,12 +335,15 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, /// Whether this declaration was an element of a capture list. IsCaptureList : 1, + + /// Whether this declaration captures the 'self' param under the same name. + IsSelfParamCapture : 1, /// Whether this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern @@ -396,7 +400,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasSingleExpressionBody : 1 ); - SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2, + SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1, /// Whether we've computed the 'static' flag yet. IsStaticComputed : 1, @@ -413,7 +417,12 @@ class alignas(1 << DeclAlignInBits) Decl { SelfAccessComputed : 1, /// Backing bits for 'self' access kind. - SelfAccess : 2 + SelfAccess : 2, + + /// Whether this is a top-level function which should be treated + /// as if it were in local context for the purposes of capture + /// analysis. + HasTopLevelLocalContextCaptures : 1 ); SWIFT_INLINE_BITFIELD(AccessorDecl, FuncDecl, 4+1+1, @@ -531,7 +540,7 @@ class alignas(1 << DeclAlignInBits) Decl { RawForeignKind : 2, /// \see ClassDecl::getEmittedMembers() - HasForcedEmittedMembers : 1, + HasForcedEmittedMembers : 1, HasMissingDesignatedInitializers : 1, ComputedHasMissingDesignatedInitializers : 1, @@ -866,6 +875,8 @@ class alignas(1 << DeclAlignInBits) Decl { LLVM_READONLY const GenericContext *getAsGenericContext() const; + bool hasUnderscoredNaming() const; + bool isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic = true) const; AvailabilityContext getAvailabilityForLinkage() const; @@ -919,7 +930,7 @@ class alignas(1 << DeclAlignInBits) Decl { // Make vanilla new/delete illegal for Decls. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; // Only allow allocation of Decls using the allocator in ASTContext // or by doing a placement new. @@ -1500,11 +1511,11 @@ enum class ImportKind : uint8_t { /// import Swift /// import typealias Swift.Int class ImportDecl final : public Decl, - private llvm::TrailingObjects> { + private llvm::TrailingObjects> { friend TrailingObjects; friend class Decl; public: - typedef std::pair AccessPathElement; + typedef Located AccessPathElement; private: SourceLoc ImportLoc; @@ -1574,9 +1585,9 @@ class ImportDecl final : public Decl, } SourceLoc getStartLoc() const { return ImportLoc; } - SourceLoc getLocFromSource() const { return getFullAccessPath().front().second; } + SourceLoc getLocFromSource() const { return getFullAccessPath().front().Loc; } SourceRange getSourceRange() const { - return SourceRange(ImportLoc, getFullAccessPath().back().second); + return SourceRange(ImportLoc, getFullAccessPath().back().Loc); } SourceLoc getKindLoc() const { return KindLoc; } @@ -2705,6 +2716,10 @@ class ValueDecl : public Decl { AccessSemantics getAccessSemanticsFromContext(const DeclContext *DC, bool isAccessOnSelf) const; + /// Determines if a reference to this declaration from a nested function + /// should be treated like a capture of a local value. + bool isLocalCapture() const; + /// Print a reference to the given declaration. std::string printRef() const; @@ -2849,7 +2864,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { /// code. One is formed implicitly when a declaration is written with an opaque /// result type, as in: /// -/// func foo() -> opaque SignedInteger { return 1 } +/// func foo() -> some SignedInteger { return 1 } /// /// The declared type is a special kind of ArchetypeType representing the /// abstracted underlying type. @@ -3278,20 +3293,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// its extensions. /// /// The table itself is lazily constructed and updated when - /// lookupDirect() is called. The bit indicates whether the lookup - /// table has already added members by walking the declarations in - /// scope; it should be manipulated through \c isLookupTablePopulated() - /// and \c setLookupTablePopulated(). - llvm::PointerIntPair LookupTable; + /// lookupDirect() is called. + MemberLookupTable *LookupTable = nullptr; /// Prepare the lookup table to make it ready for lookups. void prepareLookupTable(); - /// True if the entries in \c LookupTable are complete--that is, if a - /// name is present, it contains all members with that name. - bool isLookupTablePopulated() const; - void setLookupTablePopulated(bool value); - /// Note that we have added a member into the iterable declaration context, /// so that it can also be added to the lookup table (if needed). void addedMember(Decl *member); @@ -3319,7 +3326,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class DeclContext; friend class IterableDeclContext; friend ArrayRef - ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; + ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; protected: Type DeclaredTy; @@ -3829,6 +3836,25 @@ class ClassDecl final : public NominalTypeDecl { return None; } + Optional getCachedHasMissingDesignatedInitializers() const { + if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) { + // Force loading all the members, which will add this attribute if any of + // members are determined to be missing while loading. + auto mutableThis = const_cast(this); + (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); + } + + if (Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) + return Bits.ClassDecl.HasMissingDesignatedInitializers; + + return None; + } + + void setHasMissingDesignatedInitializers(bool value) { + Bits.ClassDecl.HasMissingDesignatedInitializers = value; + Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = true; + } + /// Marks that this class inherits convenience initializers from its /// superclass. void setInheritsSuperclassInitializers(bool value) { @@ -3839,6 +3865,7 @@ class ClassDecl final : public NominalTypeDecl { friend class SuperclassDeclRequest; friend class SuperclassTypeRequest; friend class EmittedMembersRequest; + friend class HasMissingDesignatedInitializersRequest; friend class InheritsSuperclassInitializersRequest; friend class TypeChecker; @@ -3945,11 +3972,6 @@ class ClassDecl final : public NominalTypeDecl { /// initializers that cannot be represented in Swift. bool hasMissingDesignatedInitializers() const; - void setHasMissingDesignatedInitializers(bool newValue = true) { - Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - Bits.ClassDecl.HasMissingDesignatedInitializers = newValue; - } - /// Returns true if the class has missing members that require vtable entries. /// /// In this case, the class cannot be subclassed, because we cannot construct @@ -3988,7 +4010,7 @@ class ClassDecl final : public NominalTypeDecl { /// Determine whether this class inherits the convenience initializers /// from its superclass. - bool inheritsSuperclassInitializers(); + bool inheritsSuperclassInitializers() const; /// Walks the class hierarchy starting from this class, checking various /// conditions. @@ -5029,6 +5051,12 @@ class VarDecl : public AbstractStorageDecl { /// Is this an element in a capture list? bool isCaptureList() const { return Bits.VarDecl.IsCaptureList; } + + /// Is this a capture of the self param? + bool isSelfParamCapture() const { return Bits.VarDecl.IsSelfParamCapture; } + void setIsSelfParamCapture(bool IsSelfParamCapture = true) { + Bits.VarDecl.IsSelfParamCapture = IsSelfParamCapture; + } /// Return true if this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern @@ -5841,13 +5869,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// \sa hasBody() BraceStmt *getBody(bool canSynthesize = true) const; - void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed) { - assert(getBodyKind() != BodyKind::Skipped && - "cannot set a body if it was skipped"); - - Body = S; - setBodyKind(NewBodyKind); - } + void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed); /// Note that the body was skipped for this function. Function body /// cannot be attached after this call. @@ -5866,7 +5888,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Note that parsing for the body was delayed. void setBodyDelayed(SourceRange bodyRange) { - assert(getBodyKind() == BodyKind::None); + assert(getBodyKind() == BodyKind::None || + getBodyKind() == BodyKind::Skipped); assert(bodyRange.isValid()); BodyRange = bodyRange; setBodyKind(BodyKind::Unparsed); @@ -6086,6 +6109,7 @@ class FuncDecl : public AbstractFunctionDecl { Bits.FuncDecl.SelfAccessComputed = false; Bits.FuncDecl.IsStaticComputed = false; Bits.FuncDecl.IsStatic = false; + Bits.FuncDecl.HasTopLevelLocalContextCaptures = false; } private: @@ -6243,6 +6267,12 @@ class FuncDecl : public AbstractFunctionDecl { /// Perform basic checking to determine whether the @IBAction or /// @IBSegueAction attribute can be applied to this function. bool isPotentialIBActionTarget() const; + + bool hasTopLevelLocalContextCaptures() const { + return Bits.FuncDecl.HasTopLevelLocalContextCaptures; + } + + void setHasTopLevelLocalContextCaptures(bool hasCaptures=true); }; /// This represents an accessor function, such as a getter or setter. @@ -6657,6 +6687,9 @@ class ConstructorDecl : public AbstractFunctionDecl { /// initializer. BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags, ApplyExpr **init = nullptr) const; + void clearCachedDelegatingOrChainedInitKind() { + Bits.ConstructorDecl.ComputedBodyInitKind = 0; + } /// Whether this constructor is required. bool isRequired() const { diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 6f1dcadf17031..ab691bb708ecf 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -346,7 +346,7 @@ namespace swift { std::vector ChildNotes; SourceLoc Loc; bool IsChildNote = false; - const Decl *Decl = nullptr; + const swift::Decl *Decl = nullptr; friend DiagnosticEngine; diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 1664d394dfe1b..e301e51f09daf 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -166,6 +166,9 @@ WARNING(warn_opt_remark_disabled, none, WARNING(warn_ignoring_batch_mode,none, "ignoring '-enable-batch-mode' because '%0' was also specified", (StringRef)) +WARNING(warn_ignoring_wmo, none, + "ignoring '-wmo' because '-dump-ast' was also specified", ()) + WARNING(warn_ignoring_source_range_dependencies,none, "ignoring '-enable-source-range-dependencies' because '%0' was also specified", (StringRef)) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index f739926aa92ff..049527c6fbb7f 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -214,6 +214,9 @@ ERROR(repl_must_be_initialized,none, ERROR(error_doing_code_completion,none, "compiler is in code completion mode (benign diagnostic)", ()) +WARNING(completion_reusing_astcontext,none, + "completion reusing previous ASTContext (benign diagnostic)", ()) + ERROR(verify_encountered_fatal,none, "fatal error encountered while in -verify mode", ()) @@ -340,6 +343,12 @@ ERROR(unknown_forced_module_loading_mode,none, "unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'", (StringRef)) +REMARK(interface_file_lock_failure,none, + "could not acquire lock file for module interface '%0'", (StringRef)) + +REMARK(interface_file_lock_timed_out,none, + "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index df4ddf42b9a89..d97909581b1b9 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -98,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef, ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef)) +ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef)) + +ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 9f26b49408557..fabdc9682b4f0 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1037,7 +1037,11 @@ ERROR(expected_expr_repeat_while,PointsToFirstBadToken, "expected expression in 'repeat-while' condition", ()) ERROR(do_while_now_repeat_while,none, - "'do-while' statement is not allowed; use 'repeat-while' instead", ()) + "'do-while' statement is not allowed", ()) +NOTE(do_while_expected_repeat_while, none, + "did you mean 'repeat-while' statement?", ()) +NOTE(do_while_expected_separate_stmt, none, + "did you mean separate 'do' and 'while' statements?", ()) // Do/Catch Statement ERROR(expected_lbrace_after_do,PointsToFirstBadToken, @@ -1538,21 +1542,24 @@ ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, "expected a member name as second parameter in '_implements' attribute", ()) // differentiable +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. ERROR(attr_differentiable_expected_function_name,PointsToFirstBadToken, "expected a %0 function name", (StringRef)) ERROR(attr_differentiable_expected_parameter_list,PointsToFirstBadToken, "expected a list of parameters to differentiate with respect to", ()) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. ERROR(attr_differentiable_use_wrt_not_withrespectto,none, "use 'wrt:' to specify parameters to differentiate with respect to", ()) -ERROR(attr_differentiable_missing_label,PointsToFirstBadToken, - "missing label '%0:' in '@differentiable' attribute", (StringRef)) ERROR(attr_differentiable_expected_label,none, "expected either 'wrt:' or a function specifier label, e.g. 'jvp:', " "or 'vjp:'", ()) -ERROR(differentiable_attribute_expected_rparen,none, - "expected ')' in '@differentiable' attribute", ()) -ERROR(unexpected_argument_differentiable,none, +ERROR(attr_differentiable_unexpected_argument,none, "unexpected argument '%0' in '@differentiable' attribute", (StringRef)) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. +WARNING(attr_differentiable_jvp_vjp_deprecated_warning,none, + "'jvp:' and 'vjp:' arguments in '@differentiable' attribute are " + "deprecated; use '@derivative' attribute for derivative registration " + "instead", ()) // differentiation `wrt` parameters clause ERROR(expected_colon_after_label,PointsToFirstBadToken, @@ -1560,9 +1567,12 @@ ERROR(expected_colon_after_label,PointsToFirstBadToken, ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, "expected a parameter, which can be a function parameter name, " "parameter index, or 'self'", ()) +ERROR(diff_params_clause_expected_parameter_unnamed,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter index or 'self'", + ()) -// derivative -ERROR(attr_derivative_expected_original_name,PointsToFirstBadToken, +// Automatic differentiation attributes +ERROR(autodiff_attr_expected_original_decl_name,PointsToFirstBadToken, "expected an original function name", ()) //------------------------------------------------------------------------------ diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 206b8b0d8a820..e46a33d263bc3 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -528,6 +528,9 @@ ERROR(oslog_non_constant_interpolation, none, "'OSLogInterpolation' instance " ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a " "constant", (StringRef)) +ERROR(oslog_message_alive_after_opts, none, "OSLogMessage instance must not " + "be explicitly created and must be deletable", ()) + ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer " "builtin must used only on string literals", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8f27a0d67abdb..564a75d4addd6 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -73,6 +73,10 @@ ERROR(no_overloads_match_exactly_in_assignment,none, "no exact matches in assignment to %0", (DeclBaseName)) +NOTE(candidate_partial_match,none, + "candidate has partially matching parameter list %0", + (StringRef)) + ERROR(ambiguous_subscript,none, "ambiguous subscript with base type %0 and index type %1", (Type, Type)) @@ -107,7 +111,7 @@ ERROR(could_not_find_subscript_member_did_you_mean,none, (Type)) ERROR(could_not_find_enum_case,none, - "enum type %0 has no case %1; did you mean %2", + "enum type %0 has no case %1; did you mean %2?", (Type, DeclNameRef, DeclName)) NOTE(did_you_mean_raw_type,none, @@ -274,9 +278,6 @@ ERROR(cannot_call_with_params, none, ERROR(cannot_call_non_function_value,none, "cannot call value of non-function type %0", (Type)) -ERROR(wrong_argument_labels_overload,none, - "argument labels '%0' do not match any available overloads", (StringRef)) - ERROR(no_candidates_match_result_type,none, "no '%0' candidates produce the expected contextual result type %1", (StringRef, Type)) @@ -302,11 +303,11 @@ ERROR(cannot_invoke_closure_type,none, ERROR(cannot_infer_closure_type,none, "unable to infer closure type in the current context", ()) ERROR(cannot_infer_closure_result_type,none, - "unable to infer complex closure return type; " - "add explicit type to disambiguate", ()) -FIXIT(insert_closure_return_type, - "%select{| () }1-> %0 %select{|in }1", - (Type, bool)) + "unable to infer%select{ complex|}0 closure return type; " + "add explicit type to disambiguate", (bool)) +FIXIT(insert_closure_return_type_placeholder, + "%select{| () }0-> <#Result#> %select{|in }0", + (bool)) ERROR(incorrect_explicit_closure_result,none, "declared closure result %0 is incompatible with contextual type %1", @@ -951,7 +952,7 @@ NOTE(in_cast_expr_types,none, (Type, Type)) ERROR(types_not_convertible_use_bool_value,none, - "%0 is not convertible to %1; did you mean %0.boolValue", (Type, Type)) + "%0 is not convertible to %1; did you mean %0.boolValue?", (Type, Type)) ERROR(tuple_types_not_convertible_nelts,none, "%0 is not convertible to %1, " @@ -1467,6 +1468,15 @@ NOTE(option_set_empty_set_init,none, ERROR(originally_defined_in_dupe_platform,none, "duplicate version number for platform %0", (StringRef)) +ERROR(originally_definedin_topleve_decl,none, + "@%0 is only applicable to top-level decl", (StringRef)) + +ERROR(originally_definedin_need_available,none, + "need @available attribute for @%0", (StringRef)) + +ERROR(originally_definedin_must_after_available_version,none, + "moved version from @%0 must after introduced OS version", (StringRef)) + // Alignment attribute ERROR(alignment_not_power_of_two,none, "alignment value must be a power of two", ()) @@ -3235,11 +3245,20 @@ ERROR(self_assignment_var,none, ERROR(self_assignment_prop,none, "assigning a property to itself", ()) ERROR(property_use_in_closure_without_explicit_self,none, - "reference to property %0 in closure requires explicit 'self.' to make" - " capture semantics explicit", (Identifier)) + "reference to property %0 in closure requires explicit use of 'self' to" + " make capture semantics explicit", (Identifier)) ERROR(method_call_in_closure_without_explicit_self,none, - "call to method %0 in closure requires explicit 'self.' to make" + "call to method %0 in closure requires explicit use of 'self' to make" " capture semantics explicit", (Identifier)) +NOTE(note_capture_self_explicitly,none, + "capture 'self' explicitly to enable implicit 'self' in this closure", ()) +NOTE(note_reference_self_explicitly,none, + "reference 'self.' explicitly", ()) +NOTE(note_other_self_capture,none, + "variable other than 'self' captured here under the name 'self' does not " + "enable implicit 'self'", ()) +NOTE(note_self_captured_weakly,none, + "weak capture of 'self' here does not enable implicit 'self'", ()) ERROR(implicit_use_of_self_in_closure,none, "implicit use of 'self' in closure; use 'self.' to make" " capture semantics explicit", ()) @@ -3905,6 +3924,13 @@ ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) ERROR(unreferenced_generic_parameter,none, "generic parameter '%0' is not used in function signature", (StringRef)) +ERROR(unexpected_ctype_for_non_c_convention,none, + "convention '%0' does not support the 'cType' argument label, did you " + "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " + "instead?", (StringRef, StringRef)) +ERROR(unable_to_parse_c_function_type,none, + "unable to parse '%0'; it should be a C function pointer type or a " + "block pointer type", (StringRef)) // Opaque types ERROR(unsupported_opaque_type,none, @@ -3917,6 +3943,11 @@ ERROR(opaque_type_in_protocol_requirement,none, "'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type?", ()) +// Function differentiability +ERROR(attr_only_on_parameters_of_differentiable,none, + "'%0' may only be used on parameters of '@differentiable' function " + "types", (StringRef)) + // SIL ERROR(opened_non_protocol,none, "@opened cannot be applied to non-protocol type %0", (Type)) @@ -4784,6 +4815,13 @@ NOTE(previous_function_builder_here, none, "previous function builder specified here", ()) ERROR(function_builder_arguments, none, "function builder attributes cannot have arguments", ()) +WARNING(function_builder_disabled_by_return, none, + "application of function builder %0 disabled by explicit 'return' " + "statement", (Type)) +NOTE(function_builder_remove_attr, none, + "remove the attribute to explicitly disable the function builder", ()) +NOTE(function_builder_remove_returns, none, + "remove 'return' statements to apply the function builder", ()) //------------------------------------------------------------------------------ // MARK: differentiable programming diagnostics diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index 606ebd8164382..4b4e89a8e3c43 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -187,6 +187,9 @@ class Evaluator { /// Whether to dump detailed debug info for cycles. bool debugDumpCycles; + /// Whether we're building a request dependency graph. + bool buildDependencyGraph; + /// Used to report statistics about which requests were evaluated, if /// non-null. UnifiedStatsReporter *stats = nullptr; @@ -237,7 +240,9 @@ class Evaluator { public: /// Construct a new evaluator that can emit cyclic-dependency /// diagnostics through the given diagnostics engine. - Evaluator(DiagnosticEngine &diags, bool debugDumpCycles=false); + Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph); /// Emit GraphViz output visualizing the request graph. void emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath); @@ -253,26 +258,28 @@ class Evaluator { void registerRequestFunctions(Zone zone, ArrayRef functions); - /// Evaluate the given request and produce its result, - /// consulting/populating the cache as required. - template + /// Retrieve the result produced by evaluating a request that can + /// be cached. + template::type * = nullptr> llvm::Expected operator()(const Request &request) { - // Check for a cycle. - if (checkDependency(getCanonicalRequest(request))) { - return llvm::Error( - llvm::make_unique>(request, *this)); - } + // The request can be cached, but check a predicate to determine + // whether this particular instance is cached. This allows more + // fine-grained control over which instances get cache. + if (request.isCached()) + return getResultCached(request); - // Make sure we remove this from the set of active requests once we're - // done. - SWIFT_DEFER { - assert(activeRequests.back().castTo() == request); - activeRequests.pop_back(); - }; + return getResultUncached(request); + } - // Get the result. - return getResult(request); + /// Retrieve the result produced by evaluating a request that + /// will never be cached. + template::type * = nullptr> + llvm::Expected + operator()(const Request &request) { + return getResultUncached(request); } /// Evaluate a set of requests and return their results as a tuple. @@ -340,37 +347,27 @@ class Evaluator { /// request to the \c activeRequests stack. bool checkDependency(const AnyRequest &request); - /// Retrieve the result produced by evaluating a request that can - /// be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - // The request can be cached, but check a predicate to determine - // whether this particular instance is cached. This allows more - // fine-grained control over which instances get cache. - if (request.isCached()) - return getResultCached(request); - - return getResultUncached(request); - } - - /// Retrieve the result produced by evaluating a request that - /// will never be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - return getResultUncached(request); - } - /// Produce the result of the request without caching. template llvm::Expected getResultUncached(const Request &request) { + // Check for a cycle. + if (checkDependency(getCanonicalRequest(request))) { + return llvm::Error( + llvm::make_unique>(request, *this)); + } + + // Make sure we remove this from the set of active requests once we're + // done. + SWIFT_DEFER { + assert(activeRequests.back().castTo() == request); + activeRequests.pop_back(); + }; + // Clear out the dependencies on this request; we're going to recompute // them now anyway. - dependencies.find_as(request)->second.clear(); + if (buildDependencyGraph) + dependencies.find_as(request)->second.clear(); PrettyStackTraceRequest prettyStackTrace(request); diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2f616623e3009..20ff74d15cd97 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -65,6 +65,7 @@ namespace swift { class EnumElementDecl; class CallExpr; class KeyPathExpr; + class CaptureListExpr; enum class ExprKind : uint8_t { #define EXPR(Id, Parent) Id, @@ -283,6 +284,12 @@ class alignas(8) Expr { Discriminator : 16 ); + SWIFT_INLINE_BITFIELD(AutoClosureExpr, AbstractClosureExpr, 1, + /// True if this autoclosure was built for a function conversion, and + /// not an actual @autoclosure parameter. + IsThunk : 1 + ); + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1, /// True if closure parameters were synthesized from anonymous closure /// variables. @@ -1741,17 +1748,6 @@ class DynamicSubscriptExpr final llvm::function_ref getType = [](const Expr *E) -> Type { return E->getType(); }); - /// Create a new dynamic subscript. - static DynamicSubscriptExpr *create(ASTContext &ctx, Expr *base, - SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit); - /// getIndex - Retrieve the index of the subscript expression, i.e., the /// "offset" into the base value. Expr *getIndex() const { return Index; } @@ -3593,6 +3589,18 @@ class SerializedAbstractClosureExpr : public SerializedLocalDeclContext { /// \endcode class ClosureExpr : public AbstractClosureExpr { + /// The range of the brackets of the capture list, if present. + SourceRange BracketRange; + + /// The (possibly null) VarDecl captured by this closure with the literal name + /// "self". In order to recover this information at the time of name lookup, + /// we must be able to access it from the associated DeclContext. + /// Because the DeclContext inside a closure is the closure itself (and not + /// the CaptureListExpr which would normally maintain this sort of + /// information about captured variables), we need to have some way to access + /// this information directly on the ClosureExpr. + VarDecl *CapturedSelfDecl; + /// The location of the "throws", if present. SourceLoc ThrowsLoc; @@ -3609,16 +3617,16 @@ class ClosureExpr : public AbstractClosureExpr { /// The body of the closure, along with a bit indicating whether it /// was originally just a single expression. llvm::PointerIntPair Body; - public: - ClosureExpr(ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, + ClosureExpr(SourceRange bracketRange, VarDecl *capturedSelfDecl, + ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, SourceLoc inLoc, TypeLoc explicitResultType, unsigned discriminator, DeclContext *parent) : AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false, discriminator, parent), + BracketRange(bracketRange), CapturedSelfDecl(capturedSelfDecl), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc), InLoc(inLoc), - ExplicitResultType(explicitResultType), - Body(nullptr) { + ExplicitResultType(explicitResultType), Body(nullptr) { setParameterList(params); Bits.ClosureExpr.HasAnonymousClosureVars = false; } @@ -3650,6 +3658,8 @@ class ClosureExpr : public AbstractClosureExpr { /// explicitly-specified result type. bool hasExplicitResultType() const { return ArrowLoc.isValid(); } + /// Retrieve the range of the \c '[' and \c ']' that enclose the capture list. + SourceRange getBracketRange() const { return BracketRange; } /// Retrieve the location of the \c '->' for closures with an /// explicit result type. @@ -3715,6 +3725,14 @@ class ClosureExpr : public AbstractClosureExpr { /// Is this a completely empty closure? bool hasEmptyBody() const; + /// VarDecl captured by this closure under the literal name \c self , if any. + VarDecl *getCapturedSelfDecl() const { return CapturedSelfDecl; } + + /// Whether this closure captures the \c self param in its body in such a + /// way that implicit \c self is enabled within its body (i.e. \c self is + /// captured non-weakly). + bool capturesSelfEnablingImplictSelf() const; + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Closure; } @@ -3750,6 +3768,16 @@ class AutoClosureExpr : public AbstractClosureExpr { Discriminator, Parent) { if (Body != nullptr) setBody(Body); + + Bits.AutoClosureExpr.IsThunk = false; + } + + bool isThunk() const { + return Bits.AutoClosureExpr.IsThunk; + } + + void setIsThunk(bool isThunk) { + Bits.AutoClosureExpr.IsThunk = isThunk; } SourceRange getSourceRange() const; @@ -3789,6 +3817,8 @@ struct CaptureListEntry { CaptureListEntry(VarDecl *Var, PatternBindingDecl *Init) : Var(Var), Init(Init) { } + + bool isSimpleSelfCapture() const; }; /// CaptureListExpr - This expression represents the capture list on an explicit @@ -3894,6 +3924,10 @@ class OpaqueValueExpr : public Expr { /// value to be specified later. bool isPlaceholder() const { return Bits.OpaqueValueExpr.IsPlaceholder; } + void setIsPlaceholder(bool value) { + Bits.OpaqueValueExpr.IsPlaceholder = value; + } + SourceRange getSourceRange() const { return Range; } static bool classof(const Expr *E) { diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 5f70aa56e3121..144ed20c6621e 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -109,7 +109,7 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// Make vanilla new/delete illegal. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; /// Only allow placement new. void *operator new(size_t Bytes, void *Mem) { diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 7562da8a0ed8d..e9a36ac02b19b 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -1687,6 +1687,21 @@ class GenericSignatureBuilder::PotentialArchetype { friend class GenericSignatureBuilder; }; +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(Type T) const { + return getSubjectDependentType({ })->isEqual(T); +} + +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(const GenericSignatureBuilder::PotentialArchetype *PA) const { + return getSubjectDependentType({ })->isEqual(PA->getDependentType({ })); +} + +template +bool GenericSignatureBuilder::Constraint::hasSameSubjectAs(const GenericSignatureBuilder::Constraint &C) const { + return getSubjectDependentType({ })->isEqual(C.getSubjectDependentType({ })); +} + /// Describes a requirement whose processing has been delayed for some reason. class GenericSignatureBuilder::DelayedRequirement { public: diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 30aae5b3438e7..9b9db78a3b8c7 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -190,6 +190,10 @@ class IRGenOptions { /// Passing this flag completely disables this behavior. unsigned DisableLegacyTypeInfo : 1; + /// Create metadata specializations for generic types at statically known type + /// arguments. + unsigned PrespecializeGenericMetadata : 1; + /// The path to load legacy type layouts from. StringRef ReadLegacyTypeInfoPath; @@ -255,8 +259,9 @@ class IRGenOptions { EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false), LazyInitializeClassMetadata(false), LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), - UseIncrementalLLVMCodeGen(true), UseSwiftCall(false), - GenerateProfile(false), EnableDynamicReplacementChaining(false), + PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), + UseSwiftCall(false), GenerateProfile(false), + EnableDynamicReplacementChaining(false), DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All) {} diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index f0106a68db56f..05b595d3953eb 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -402,9 +402,7 @@ class DeclName { size_t NumArgs; explicit CompoundDeclName(DeclBaseName BaseName, size_t NumArgs) - : BaseName(BaseName), NumArgs(NumArgs) { - assert(NumArgs > 0 && "Should use IdentifierAndCompound"); - } + : BaseName(BaseName), NumArgs(NumArgs) { } ArrayRef getArgumentNames() const { return {getTrailingObjects(), NumArgs}; @@ -422,16 +420,12 @@ class DeclName { } }; - // A single stored identifier, along with a bit stating whether it is the - // base name for a zero-argument compound name. - typedef llvm::PointerIntPair BaseNameAndCompound; - - // Either a single identifier piece stored inline (with a bit to say whether - // it is simple or compound), or a reference to a compound declaration name. - llvm::PointerUnion SimpleOrCompound; + /// Either a single identifier piece stored inline, or a reference to a + /// compound declaration name. + llvm::PointerUnion BaseNameOrCompound; explicit DeclName(void *Opaque) - : SimpleOrCompound(decltype(SimpleOrCompound)::getFromOpaqueValue(Opaque)) + : BaseNameOrCompound(decltype(BaseNameOrCompound)::getFromOpaqueValue(Opaque)) {} void initialize(ASTContext &C, DeclBaseName baseName, @@ -439,11 +433,11 @@ class DeclName { public: /// Build a null name. - DeclName() : SimpleOrCompound(BaseNameAndCompound()) {} + DeclName() : BaseNameOrCompound(DeclBaseName()) {} /// Build a simple value name with one component. /*implicit*/ DeclName(DeclBaseName simpleName) - : SimpleOrCompound(BaseNameAndCompound(simpleName, false)) {} + : BaseNameOrCompound(simpleName) {} /*implicit*/ DeclName(Identifier simpleName) : DeclName(DeclBaseName(simpleName)) {} @@ -462,10 +456,10 @@ class DeclName { /// such as the 'foo' in 'func foo(x:Int, y:Int)' or the 'bar' in /// 'var bar: Int'. DeclBaseName getBaseName() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->BaseName; - return SimpleOrCompound.get().getPointer(); + return BaseNameOrCompound.get(); } /// Assert that the base name is not special and return its identifier. @@ -478,7 +472,7 @@ class DeclName { /// Retrieve the names of the arguments, if there are any. ArrayRef getArgumentNames() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->getArgumentNames(); return { }; @@ -487,25 +481,19 @@ class DeclName { bool isSpecial() const { return getBaseName().isSpecial(); } explicit operator bool() const { - if (SimpleOrCompound.dyn_cast()) + if (BaseNameOrCompound.dyn_cast()) return true; - return !SimpleOrCompound.get().getPointer().empty(); + return !BaseNameOrCompound.get().empty(); } /// True if this is a simple one-component name. bool isSimpleName() const { - if (SimpleOrCompound.dyn_cast()) - return false; - - return !SimpleOrCompound.get().getInt(); + return BaseNameOrCompound.is(); } /// True if this is a compound name. bool isCompoundName() const { - if (SimpleOrCompound.dyn_cast()) - return true; - - return SimpleOrCompound.get().getInt(); + return !isSimpleName(); } /// True if this name is a simple one-component name identical to the @@ -540,7 +528,7 @@ class DeclName { /// matches a simple name lookup or when the full compound name matches. bool matchesRef(DeclName refName) const { // Identical names always match. - if (SimpleOrCompound == refName.SimpleOrCompound) + if (BaseNameOrCompound == refName.BaseNameOrCompound) return true; // If the reference is a simple name, try simple name matching. if (refName.isSimpleName()) @@ -594,7 +582,7 @@ class DeclName { return lhs.compare(rhs) >= 0; } - void *getOpaqueValue() const { return SimpleOrCompound.getOpaqueValue(); } + void *getOpaqueValue() const { return BaseNameOrCompound.getOpaqueValue(); } static DeclName getFromOpaqueValue(void *p) { return DeclName(p); } /// Get a string representation of the name, @@ -913,7 +901,7 @@ namespace llvm { static inline swift::DeclName getFromVoidPointer(void *ptr) { return swift::DeclName::getFromOpaqueValue(ptr); } - enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 2 }; + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 1 }; }; // DeclNames hash just like pointers. diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 456fa92a53dad..09a04adbde89c 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -132,14 +132,14 @@ enum class ResilienceStrategy : unsigned { /// \sa FileUnit class ModuleDecl : public DeclContext, public TypeDecl { public: - typedef ArrayRef> AccessPathTy; + typedef ArrayRef> AccessPathTy; typedef std::pair ImportedModule; static bool matchesAccessPath(AccessPathTy AccessPath, DeclName Name) { assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); return AccessPath.empty() - || DeclName(AccessPath.front().first).matchesRef(Name); + || DeclName(AccessPath.front().Item).matchesRef(Name); } /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. @@ -582,7 +582,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { private: // Make placement new and vanilla new/delete illegal for Modules. void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) throw() = delete; void *operator new(size_t Bytes, void *Mem) throw() = delete; public: // Only allow allocation of Modules using the allocator in ASTContext @@ -629,6 +629,11 @@ inline bool DeclContext::isModuleScopeContext() const { return isModuleContext(); } +/// Extract the source location from the given module declaration. +inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) { + return extractNearestSourceLoc(static_cast(mod)); +} + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index af1d372659b69..d149611308e74 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -19,6 +19,7 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" @@ -100,7 +101,7 @@ class ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) = 0; + virtual bool canImportModule(Located named) = 0; /// Import a module with the given module path. /// @@ -113,7 +114,7 @@ class ModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule(SourceLoc importLoc, - ArrayRef> path) = 0; + ArrayRef> path) = 0; /// Load extensions to the given nominal type. /// diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 552a4be7f5e27..fb0c7fc716056 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -70,13 +70,15 @@ struct LookupResultEntry { /// When finding \c bar() from the function body of \c foo(), \c BaseDC is /// the method \c foo(). /// - /// \c BaseDC will be the method if \c self is needed for the lookup, - /// and will be the type if not. - /// In other words: If \c baseDC is a method, it means you found an instance - /// member and you should add an implicit 'self.' (Each method has its own - /// implicit self decl.) There's one other kind of non-method context that - /// has a 'self.' -- a lazy property initializer, which unlike a non-lazy - /// property can reference \c self) Hence: \code + /// \c BaseDC will be the type if \c self is not needed for the lookup. If + /// \c self is needed, \c baseDC will be either the method or a closure + /// which explicitly captured \c self. + /// In other words: If \c baseDC is a method or a closure, it means you + /// found an instance member and you should add an implicit 'self.' (Each + /// method has its own implicit self decl.) There's one other kind of + /// non-method, non-closure context that has a 'self.' -- a lazy property + /// initializer, which unlike a non-lazy property can reference \c self. + /// \code /// class Outer { ///   static func s() ///   func i() @@ -493,7 +495,7 @@ void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name, void getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject); /// Retrieve the set of nominal type declarations that are directly @@ -501,7 +503,7 @@ void getDirectlyInheritedNominalTypeDecls( /// and splitting out the components of compositions. /// /// If we come across the AnyObject type, set \c anyObject true. -SmallVector, 4> +SmallVector, 4> getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject); diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index d1ed1fd5a06cd..dbc9ddb796200 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -161,6 +161,29 @@ class SuperclassDeclRequest : void cacheResult(ClassDecl *value) const; }; +/// Requests whether or not this class has designated initializers that are +/// not public or @usableFromInline. +class HasMissingDesignatedInitializersRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ClassDecl *subject) const; + +public: + // Caching + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(bool) const; +}; + /// Request the nominal declaration extended by a given extension declaration. class ExtendedNominalRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + const DeclContext *DC, + ModuleDecl *mod, DeclNameRef name, + NLOptions opts) const; +}; + +class QualifiedLookupRequest + : public SimpleRequest, + DeclNameRef, NLOptions), + CacheKind::Uncached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, const DeclContext *DC, + SmallVector decls, + DeclNameRef name, + NLOptions opts) const; +}; + #define SWIFT_TYPEID_ZONE NameLookup #define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index c0cf8900243c5..f506dafb911f7 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -15,6 +15,9 @@ // //===----------------------------------------------------------------------===// +SWIFT_REQUEST(NameLookup, AnyObjectLookupRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, CustomAttrNominalRequest, NominalTypeDecl *(CustomAttr *, DeclContext *), Cached, NoLocationInfo) @@ -34,11 +37,28 @@ SWIFT_REQUEST(NameLookup, InheritedDeclsReferencedRequest, DirectlyReferencedTypeDecls( llvm::PointerUnion, unsigned), Uncached, HasNearestLocation) +SWIFT_REQUEST(NameLookup, LookupInModuleRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLKind, + namelookup::ResolutionKind, + const DeclContext *), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, ModuleQualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, ModuleDecl *, DeclNameRef, NLOptions), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, QualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, SmallVector, + DeclNameRef, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest, SelfBounds(llvm::PointerUnion), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SuperclassDeclRequest, ClassDecl *(NominalTypeDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, HasMissingDesignatedInitializersRequest, + bool(ClassDecl *), + SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(NameLookup, TypeDeclsFromWhereClauseRequest, DirectlyReferencedTypeDecls(ExtensionDecl *), Uncached, NoLocationInfo) @@ -48,11 +68,3 @@ SWIFT_REQUEST(NameLookup, UnderlyingTypeDeclsReferencedRequest, SWIFT_REQUEST(NameLookup, UnqualifiedLookupRequest, LookupResult(UnqualifiedLookupDescriptor), Uncached, NoLocationInfo) -SWIFT_REQUEST(NameLookup, LookupInModuleRequest, - QualifiedLookupResult(const DeclContext *, DeclName, NLKind, - namelookup::ResolutionKind, - const DeclContext *), - Uncached, NoLocationInfo) -SWIFT_REQUEST(NameLookup, AnyObjectLookupRequest, - QualifiedLookupResult(const DeclContext *, DeclName, NLOptions), - Uncached, NoLocationInfo) diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index a0f6345910d50..4bf10c283ed1d 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -756,7 +756,93 @@ inline Pattern *Pattern::getSemanticsProvidingPattern() { return vp->getSubPattern()->getSemanticsProvidingPattern(); return this; } - + +/// Describes a pattern and the context in which it occurs. +class ContextualPattern { + /// The pattern and whether this is the top-level pattern. + llvm::PointerIntPair patternAndTopLevel; + + /// Either the declaration context or the enclosing pattern binding + /// declaration. + llvm::PointerUnion declOrContext; + + /// Index into the pattern binding declaration, when there is one. + unsigned index = 0; + + ContextualPattern( + Pattern *pattern, bool topLevel, + llvm::PointerUnion declOrContext, + unsigned index + ) : patternAndTopLevel(pattern, topLevel), + declOrContext(declOrContext), + index(index) { } + +public: + /// Produce a contextual pattern for a pattern binding declaration entry. + static ContextualPattern forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index); + + /// Produce a contextual pattern for a raw pattern that always allows + /// inference. + static ContextualPattern forRawPattern(Pattern *pattern, DeclContext *dc) { + return ContextualPattern(pattern, /*topLevel=*/true, dc, /*index=*/0); + } + + /// Retrieve a contextual pattern for the given subpattern. + ContextualPattern forSubPattern( + Pattern *subpattern, bool retainTopLevel) const { + return ContextualPattern( + subpattern, isTopLevel() && retainTopLevel, declOrContext, index); + } + + /// Retrieve the pattern. + Pattern *getPattern() const { + return patternAndTopLevel.getPointer(); + } + + /// Whether this is the top-level pattern in this context. + bool isTopLevel() const { + return patternAndTopLevel.getInt(); + } + + /// Retrieve the declaration context of the pattern. + DeclContext *getDeclContext() const; + + /// Retrieve the pattern binding declaration that owns this pattern, if + /// there is one. + PatternBindingDecl *getPatternBindingDecl() const; + + /// Retrieve the index into the pattern binding declaration for the top-level + /// pattern. + unsigned getPatternBindingIndex() const { + assert(getPatternBindingDecl() != nullptr); + return index; + } + + /// Whether this pattern allows type inference, e.g., from an initializer + /// expression. + bool allowsInference() const; + + friend llvm::hash_code hash_value(const ContextualPattern &pattern) { + return llvm::hash_combine(pattern.getPattern(), + pattern.isTopLevel(), + pattern.declOrContext); + } + + friend bool operator==(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return lhs.patternAndTopLevel == rhs.patternAndTopLevel && + lhs.declOrContext == rhs.declOrContext; + } + + friend bool operator!=(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const ContextualPattern &pattern); + } // end namespace swift #endif diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6ed26109f5659..55aff0da73aca 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -306,8 +306,21 @@ struct PrintOptions { /// List of decls that should be printed even if they are implicit and \c SkipImplicit is set to true. std::vector TreatAsExplicitDeclList; + enum class FunctionRepresentationMode : uint8_t { + /// Print the entire convention, including an arguments. + /// For example, this will print a cType argument label if applicable. + Full, + /// Print only the name of the convention, skipping extra argument labels. + NameOnly, + /// Skip printing the @convention(..) altogether. + None + }; + /// Whether to print function @convention attribute on function types. - bool PrintFunctionRepresentationAttrs = true; + // FIXME: [clang-function-type-serialization] Once we start serializing Clang + // types, we should also start printing the full type in the swiftinterface. + FunctionRepresentationMode PrintFunctionRepresentationAttrs = + FunctionRepresentationMode::NameOnly; /// Whether to print storage representation attributes on types, e.g. /// '@sil_weak', '@sil_unmanaged'. @@ -367,6 +380,13 @@ struct PrintOptions { ArgAndParamPrintingMode ArgAndParamPrinting = ArgAndParamPrintingMode::MatchSource; + /// Whether to print the default argument value string + /// representation. + bool PrintDefaultArgumentValue = true; + + /// Whether to print "_" placeholders for empty arguments. + bool PrintEmptyArgumentNames = true; + /// Whether to print documentation comments attached to declarations. /// Note that this may print documentation comments from related declarations /// (e.g. the overridden method in the superclass) if such comment is found. @@ -429,7 +449,7 @@ struct PrintOptions { /// and constructors) will be printed by this function. std::function FunctionBody; - BracketOptions BracketOptions; + swift::BracketOptions BracketOptions; // This is explicit to guarantee that it can be called from LLDB. PrintOptions() {} @@ -502,7 +522,8 @@ struct PrintOptions { /// consistent and well-formed. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr); + static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention); /// Retrieve the set of options suitable for "Generated Interfaces", which /// are a prettified representation of the public API of a module, to be @@ -585,7 +606,8 @@ struct PrintOptions { PO.SkipUnderscoredKeywords = true; PO.EnumRawValues = EnumRawValueMode::Print; PO.PrintImplicitAttrs = false; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.PrintDocumentationComments = false; PO.ExcludeAttrList.push_back(DAK_Available); PO.SkipPrivateStdlibDecls = true; diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1b8d4d8879f32..f5fa29f0f6be4 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -285,7 +285,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { // Make vanilla new/delete illegal for protocol conformances. void *operator new(size_t bytes) = delete; - void operator delete(void *data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *data) = delete; // Only allow allocation of protocol conformances using the allocator in // ASTContext or by doing a placement new. diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 43899fbafa4d2..61173f8efefe4 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -125,15 +125,42 @@ class SourceFile final : public FileUnit { /// been validated. llvm::SetVector UnvalidatedDeclsWithOpaqueReturnTypes; + /// The list of top-level declarations in the source file. + std::vector Decls; + friend ASTContext; friend Impl; + public: - /// The list of top-level declarations in the source file. - std::vector Decls; + /// Appends the given declaration to the end of the top-level decls list. + void addTopLevelDecl(Decl *d) { + Decls.push_back(d); + } + + /// Prepends a declaration to the top-level decls list. + /// + /// FIXME: This entrypoint exists to support LLDB. Calls to this function are + /// always a mistake, and additional uses should not be added. + /// + /// See rdar://58355191 + void prependTopLevelDecl(Decl *d) { + Decls.insert(Decls.begin(), d); + } + + /// Retrieves an immutable view of the list of top-level decls in this file. + ArrayRef getTopLevelDecls() const { + return Decls; + } + + /// Truncates the list of top-level decls so it contains \c count elements. + void truncateTopLevelDecls(unsigned count) { + assert(count <= Decls.size() && "Can only truncate top-level decls!"); + Decls.resize(count); + } /// A cache of syntax nodes that can be reused when creating the syntax tree /// for this file. - SyntaxParsingCache *SyntaxParsingCache = nullptr; + swift::SyntaxParsingCache *SyntaxParsingCache = nullptr; /// The list of local type declarations in the source file. llvm::SetVector LocalTypeDecls; @@ -404,9 +431,11 @@ class SourceFile final : public FileUnit { InterfaceHash->update(a); } - void getInterfaceHash(llvm::SmallString<32> &str) { + void getInterfaceHash(llvm::SmallString<32> &str) const { + // Copy to preserve idempotence. + llvm::MD5 md5 = *InterfaceHash; llvm::MD5::MD5Result result; - InterfaceHash->final(result); + md5.final(result); llvm::MD5::stringifyResult(result, str); } diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 8031a2188d819..9a762df4f6de2 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -808,8 +808,6 @@ class ForEachStmt : public LabeledStmt { // Set by Sema: ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef(); - ConcreteDeclRef makeIterator; - ConcreteDeclRef iteratorNext; VarDecl *iteratorVar = nullptr; Expr *iteratorVarRef = nullptr; OpaqueValueExpr *elementExpr = nullptr; @@ -838,12 +836,6 @@ class ForEachStmt : public LabeledStmt { void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; } Expr *getConvertElementExpr() const { return convertElementExpr; } - void setMakeIterator(ConcreteDeclRef declRef) { makeIterator = declRef; } - ConcreteDeclRef getMakeIterator() const { return makeIterator; } - - void setIteratorNext(ConcreteDeclRef declRef) { iteratorNext = declRef; } - ConcreteDeclRef getIteratorNext() const { return iteratorNext; } - void setSequenceConformance(ProtocolConformanceRef conformance) { sequenceConformance = conformance; } diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index 0ae98500eb524..deb897f5053bb 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -52,11 +52,10 @@ enum class CombineSubstitutionMaps { /// any entity that can reference type parameters, e.g., types (via /// Type::subst()) and conformances (via ProtocolConformanceRef::subst()). /// -/// SubstitutionMaps are constructed by calling the getSubstitutionMap() method -/// on a GenericSignature or (equivalently) by calling one of the static -/// \c SubstitutionMap::get() methods. However, most substitution maps are +/// SubstitutionMaps are constructed by calling the an overload of the static +/// method \c SubstitutionMap::get(). However, most substitution maps are /// computed using higher-level entry points such as -/// TypeBase::getMemberSubstitutionMap(). +/// TypeBase::getContextSubstitutionMap(). /// /// Substitution maps are ASTContext-allocated and are uniqued on construction, /// so they can be used as fields in AST nodes. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 1704b817d85c6..2eb562c497958 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -16,10 +16,12 @@ #ifndef SWIFT_TYPE_CHECK_REQUESTS_H #define SWIFT_TYPE_CHECK_REQUESTS_H +#include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" +#include "swift/AST/Pattern.h" #include "swift/AST/SimpleRequest.h" #include "swift/AST/TypeResolutionStage.h" #include "swift/Basic/AnyValue.h" @@ -33,6 +35,7 @@ namespace swift { class AbstractStorageDecl; class AccessorDecl; enum class AccessorKind; +class ContextualPattern; class DefaultArgumentExpr; class GenericParamList; class PrecedenceGroupDecl; @@ -1758,7 +1761,7 @@ enum class FunctionBuilderClosurePreCheck : uint8_t { class PreCheckFunctionBuilderRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -1768,7 +1771,7 @@ class PreCheckFunctionBuilderRequest // Evaluation. llvm::Expected - evaluate(Evaluator &evaluator, ClosureExpr *closure) const; + evaluate(Evaluator &evaluator, AnyFunctionRef fn) const; public: // Separate caching. @@ -1991,6 +1994,32 @@ class HasDynamicMemberLookupAttributeRequest } }; +/// Determines the type of a given pattern. +/// +/// Note that this returns the "raw" pattern type, which can involve +/// unresolved types and unbound generic types where type inference is +/// allowed. +class PatternTypeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate( + Evaluator &evaluator, ContextualPattern pattern) const; + +public: + bool isCached() const { return true; } + + SourceLoc getNearestLoc() const { + return std::get<0>(getStorage()).getPattern()->getLoc(); + } +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 2e9f6036e4d11..a5e4df61a61cb 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -196,7 +196,7 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(ClosureExpr *), + FunctionBuilderClosurePreCheck(AnyFunctionRef), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, bool(NominalTypeDecl *, ImplicitMemberAction), Uncached, @@ -216,3 +216,6 @@ SWIFT_REQUEST(TypeChecker, TypeWitnessRequest, SWIFT_REQUEST(TypeChecker, ValueWitnessRequest, Witness(NormalProtocolConformance *, ValueDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PatternTypeRequest, + Type(ContextualPattern), + Cached, HasNearestLocation) diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index e4cce80d12353..56e4995aad08c 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -26,6 +26,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "swift/Basic/Debug.h" +#include "swift/Basic/Located.h" #include "swift/Basic/InlineBitfield.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" @@ -672,9 +673,9 @@ struct TupleTypeReprElement { /// \endcode class TupleTypeRepr final : public TypeRepr, private llvm::TrailingObjects> { + Located> { friend TrailingObjects; - typedef std::pair SourceLocAndIdx; + typedef Located SourceLocAndIdx; SourceRange Parens; @@ -745,12 +746,12 @@ class TupleTypeRepr final : public TypeRepr, SourceLoc getEllipsisLoc() const { return hasEllipsis() ? - getTrailingObjects()[0].first : SourceLoc(); + getTrailingObjects()[0].Loc : SourceLoc(); } unsigned getEllipsisIndex() const { return hasEllipsis() ? - getTrailingObjects()[0].second : + getTrailingObjects()[0].Item : Bits.TupleTypeRepr.NumElements; } @@ -758,8 +759,8 @@ class TupleTypeRepr final : public TypeRepr, if (hasEllipsis()) { Bits.TupleTypeRepr.HasEllipsis = false; getTrailingObjects()[0] = { - SourceLoc(), - getNumElements() + getNumElements(), + SourceLoc() }; } } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 8943ea372446b..bf066910de272 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -59,6 +59,7 @@ class AssociatedTypeDecl; class ASTContext; enum BufferPointerTypeKind : unsigned; class ClassDecl; +class ClangModuleLoader; class DependentMemberType; class GenericTypeParamDecl; class GenericTypeParamType; @@ -1808,8 +1809,8 @@ class ParameterTypeFlags { NonEphemeral = 1 << 2, OwnershipShift = 3, Ownership = 7 << OwnershipShift, - - NumBits = 6 + NoDerivative = 1 << 7, + NumBits = 7 }; OptionSet value; static_assert(NumBits < 8*sizeof(OptionSet), "overflowed"); @@ -1823,15 +1824,17 @@ class ParameterTypeFlags { } ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral, - ValueOwnership ownership) + ValueOwnership ownership, bool noDerivative) : value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) | (nonEphemeral ? NonEphemeral : 0) | - uint8_t(ownership) << OwnershipShift) {} + uint8_t(ownership) << OwnershipShift | + (noDerivative ? NoDerivative : 0)) {} /// Create one from what's present in the parameter type inline static ParameterTypeFlags fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure, - bool isNonEphemeral, ValueOwnership ownership); + bool isNonEphemeral, ValueOwnership ownership, + bool isNoDerivative); bool isNone() const { return !value; } bool isVariadic() const { return value.contains(Variadic); } @@ -1840,6 +1843,7 @@ class ParameterTypeFlags { bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; } bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;} bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; } + bool isNoDerivative() const { return value.contains(NoDerivative); } ValueOwnership getValueOwnership() const { return ValueOwnership((value.toRaw() & Ownership) >> OwnershipShift); @@ -1882,6 +1886,12 @@ class ParameterTypeFlags { : value - ParameterTypeFlags::NonEphemeral); } + ParameterTypeFlags withNoDerivative(bool noDerivative) const { + return ParameterTypeFlags(noDerivative + ? value | ParameterTypeFlags::NoDerivative + : value - ParameterTypeFlags::NoDerivative); + } + bool operator ==(const ParameterTypeFlags &other) const { return value.toRaw() == other.value.toRaw(); } @@ -1948,8 +1958,8 @@ class YieldTypeFlags { ParameterTypeFlags asParamFlags() const { return ParameterTypeFlags(/*variadic*/ false, /*autoclosure*/ false, - /*nonEphemeral*/ false, - getValueOwnership()); + /*nonEphemeral*/ false, getValueOwnership(), + /*noDerivative*/ false); } bool operator ==(const YieldTypeFlags &other) const { @@ -2821,6 +2831,9 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked '@_nonEphemeral' bool isNonEphemeral() const { return Flags.isNonEphemeral(); } + /// Whether the parameter is marked '@noDerivative'. + bool isNoDerivative() const { return Flags.isNoDerivative(); } + ValueOwnership getValueOwnership() const { return Flags.getValueOwnership(); } @@ -2938,6 +2951,11 @@ class AnyFunctionType : public TypeBase { bool empty() const { return !ClangFunctionType; } Uncommon(const clang::Type *type) : ClangFunctionType(type) {} + + public: + /// Use the ClangModuleLoader to print the Clang type as a string. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os); }; private: @@ -3882,6 +3900,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, bool empty() const { return !ClangFunctionType; } Uncommon(const clang::FunctionType *type) : ClangFunctionType(type) {} + + public: + /// Analog of AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os) const; }; Uncommon Other; @@ -3935,6 +3958,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getSILFunctionLanguage(getRepresentation()); } + /// Return the underlying Uncommon value if it is not the default value. + Optional getUncommonInfo() const { + return Other.empty() ? Optional() : Other; + } + bool hasSelfParam() const { switch (getRepresentation()) { case Representation::Thick: @@ -5818,10 +5846,9 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const { } /// Create one from what's present in the parameter decl and type -inline ParameterTypeFlags -ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, - bool isAutoClosure, bool isNonEphemeral, - ValueOwnership ownership) { +inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( + Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, + ValueOwnership ownership, bool isNoDerivative) { // FIXME(Remove InOut): The last caller that needs this is argument // decomposition. Start by enabling the assertion there and fixing up those // callers, then remove this, then remove @@ -5831,7 +5858,7 @@ ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, ownership == ValueOwnership::InOut); ownership = ValueOwnership::InOut; } - return {isVariadic, isAutoClosure, isNonEphemeral, ownership}; + return {isVariadic, isAutoClosure, isNonEphemeral, ownership, isNoDerivative}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index eea6c937c8ef8..b422175695ade 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -27,15 +27,6 @@ #define __has_attribute(x) 0 #endif -#if SWIFT_COMPILER_IS_MSVC && _MSC_VER < 1910 -// Work around MSVC bug: attempting to reference a deleted function -// https://connect.microsoft.com/VisualStudio/feedback/details/3116505 -#define SWIFT_DELETE_OPERATOR_DELETED \ - { llvm_unreachable("Delete operator should not be called."); } -#else -#define SWIFT_DELETE_OPERATOR_DELETED = delete; -#endif - // __builtin_assume() is an optimization hint. #if __has_builtin(__builtin_assume) #define SWIFT_ASSUME(x) __builtin_assume(x) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 8d24bd41d831b..0795566e66c58 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -173,7 +173,10 @@ namespace swift { /// Whether to dump debug info for request evaluator cycles. bool DebugDumpCycles = false; - + + /// Whether to build a request dependency graph for debugging. + bool BuildRequestDependencyGraph = false; + /// Enable SIL type lowering bool EnableSubstSILFunctionTypesForFunctionValues = false; diff --git a/include/swift/Basic/Lazy.h b/include/swift/Basic/Lazy.h index 594e2ed8b9fcd..647e5aff41c59 100644 --- a/include/swift/Basic/Lazy.h +++ b/include/swift/Basic/Lazy.h @@ -16,12 +16,18 @@ #include #ifdef __APPLE__ #include +#elif defined(__wasi__) +// No pthread on wasi #else #include #endif #include "swift/Basic/Malloc.h" #include "swift/Basic/type_traits.h" +#ifdef __wasi__ +void wasi_polyfill_call_once(int *flag, void *context, void (*func)(void *)); +#endif + namespace swift { #ifdef __APPLE__ @@ -36,6 +42,10 @@ namespace swift { using OnceToken_t = unsigned long; # define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ _swift_once_f(&TOKEN, CONTEXT, FUNC) +#elif defined(__wasi__) + using OnceToken_t = int; +# define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ + ::wasi_polyfill_call_once(&TOKEN, CONTEXT, FUNC) #else using OnceToken_t = std::once_flag; # define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \ diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h new file mode 100644 index 0000000000000..6b3430b365124 --- /dev/null +++ b/include/swift/Basic/Located.h @@ -0,0 +1,85 @@ +//===--- Located.h - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Provides a currency data type Located that should be used instead +// of std::pair. +// +//===----------------------------------------------------------------------===// + + +#ifndef SWIFT_BASIC_LOCATED_H +#define SWIFT_BASIC_LOCATED_H +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/SourceLoc.h" + +namespace swift { + +/// A currency type for keeping track of items which were found in the source code. +/// Several parts of the compiler need to keep track of a `SourceLoc` corresponding +/// to an item, in case they need to report some diagnostics later. For example, +/// the ClangImporter needs to keep track of where imports were originally written. +/// Located makes it easy to do so while making the code more readable, compared to +/// using `std::pair`. +template +struct Located { + /// The main item whose source location is being tracked. + T Item; + + /// The original source location from which the item was parsed. + SourceLoc Loc; + + Located() : Item(), Loc() {} + + Located(T Item, SourceLoc loc) : Item(Item), Loc(loc) {} + + SWIFT_DEBUG_DUMP; + void dump(raw_ostream &os) const; +}; + +template +bool operator ==(const Located &lhs, const Located &rhs) { + return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; +} + +} // end namespace swift + +namespace llvm { + +template struct DenseMapInfo; + +template +struct DenseMapInfo> { + + static inline swift::Located getEmptyKey() { + return swift::Located(DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + + static inline swift::Located getTombstoneKey() { + return swift::Located(DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const swift::Located &LocatedVal) { + return combineHashValue(DenseMapInfo::getHashValue(LocatedVal.Item), + DenseMapInfo::getHashValue(LocatedVal.Loc)); + } + + static bool isEqual(const swift::Located &LHS, const swift::Located &RHS) { + return DenseMapInfo::isEqual(LHS.Item, RHS.Item) && + DenseMapInfo::isEqual(LHS.Loc, RHS.Loc); + } +}; +} // namespace llvm + +#endif // SWIFT_BASIC_LOCATED_H diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index 04ee3253990e9..cfd9abebd4a00 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -91,7 +91,9 @@ namespace swift { /// llvm::Triple::normalize() would not affect it. llvm::Triple getTargetSpecificModuleTriple(const llvm::Triple &triple); - + /// Computes the target triple without version information. + llvm::Triple getUnversionedTriple(const llvm::Triple &triple); + /// Get the Swift runtime version to deploy back to, given a deployment target expressed as an /// LLVM target triple. Optional diff --git a/include/swift/Basic/RelativePointer.h b/include/swift/Basic/RelativePointer.h index f5cffd1c33c25..31cfd2971ece2 100644 --- a/include/swift/Basic/RelativePointer.h +++ b/include/swift/Basic/RelativePointer.h @@ -146,6 +146,11 @@ static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) { std::is_signed::value, "offset type should be signed integer"); +#ifdef __wasm__ + // WebAssembly: hack: disable relative pointers + return (uintptr_t)(intptr_t)offset; +#endif + auto base = reinterpret_cast(basePtr); // We want to do wrapping arithmetic, but with a sign-extended // offset. To do this in C, we need to do signed promotion to get @@ -164,6 +169,13 @@ static inline Offset measureRelativeOffset(A *referent, B *base) { static_assert(std::is_integral::value && std::is_signed::value, "offset type should be signed integer"); +#ifdef __wasm__ + // WebAssembly: hack: disable relative pointers + auto offset = (Offset)(uintptr_t)referent; + assert((intptr_t)offset == (intptr_t)(uintptr_t)referent + && "pointer too large to fit in offset type"); + return offset; +#endif auto distance = (uintptr_t)referent - (uintptr_t)base; // Truncate as unsigned, then wrap around to signed. diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 725c082b8d877..657568f3ea3a0 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -138,12 +138,6 @@ FRONTEND_STATISTIC(AST, NumPrefixOperators) /// Number of precedence groups in the AST context. FRONTEND_STATISTIC(AST, NumPrecedenceGroups) -/// Number of qualified lookups into a nominal type. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInNominal) - -/// Number of qualified lookups into a module. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInModule) - /// Number of local lookups into a module. FRONTEND_STATISTIC(AST, NumModuleLookupValue) diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 6907d8363d6f0..26c17e4627e90 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -149,6 +149,16 @@ struct SupplementaryOutputPaths { /// \sa swift::emitSwiftInterface std::string ModuleInterfaceOutputPath; + /// The path to a .c file where we should declare $ld$add symbols for those + /// symbols moved to the current module. + /// When symbols are moved to this module, this module declares them as HIDE + /// for the OS versions prior to when the move happened. On the other hand, the + /// original module should ADD them for these OS versions. An executable + /// can choose the right library to link against depending on the deployment target. + /// This is a walk-around that linker directives cannot specify other install + /// name per symbol, we should eventually remove this. + std::string LdAddCFilePath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -158,7 +168,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty(); + ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); } }; } // namespace swift diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9150852c0088a..9ac0d73ef7b9b 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -40,6 +40,7 @@ namespace clang { class NamedDecl; class Sema; class TargetInfo; + class Type; class VisibleDeclConsumer; class DeclarationName; } @@ -173,7 +174,7 @@ class ClangImporter final : public ClangModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -189,7 +190,7 @@ class ClangImporter final : public ClangModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule( SourceLoc importLoc, - ArrayRef> path) + ArrayRef> path) override; /// Determine whether \c overlayDC is within an overlay module for the @@ -399,7 +400,7 @@ class ClangImporter final : public ClangModuleLoader { /// Given the path of a Clang module, collect the names of all its submodules. /// Calling this function does not load the module. void collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const; /// Given a Clang module, decide whether this module is imported already. @@ -416,6 +417,11 @@ class ClangImporter final : public ClangModuleLoader { /// with -import-objc-header option. getPCHFilename(const ClangImporterOptions &ImporterOptions, StringRef SwiftPCHHash, bool &isExplicit); + + const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const override; + void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/ClangImporter/ClangImporterOptions.h b/include/swift/ClangImporter/ClangImporterOptions.h index 4bf47c4d3575f..14e593daf88a0 100644 --- a/include/swift/ClangImporter/ClangImporterOptions.h +++ b/include/swift/ClangImporter/ClangImporterOptions.h @@ -99,6 +99,10 @@ class ClangImporterOptions { /// When set, don't enforce warnings with -Werror. bool DebuggerSupport = false; + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport = false; + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Config.h.in b/include/swift/Config.h.in index 0b5534cf7424f..ec8030b3d1b03 100644 --- a/include/swift/Config.h.in +++ b/include/swift/Config.h.in @@ -6,8 +6,6 @@ #cmakedefine SWIFT_HAVE_WORKING_STD_REGEX 1 -#cmakedefine HAVE_UNICODE_LIBEDIT 1 - #cmakedefine HAVE_WAIT4 1 #cmakedefine HAVE_PROC_PID_RUSAGE 1 diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index b4ee935eb71db..c17f157976aa8 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -563,7 +563,6 @@ class Demangler : public NodeFactory { NodePointer demangleGenericType(); NodePointer demangleValueWitness(); - NodePointer demangleObjCTypeName(); NodePointer demangleTypeMangling(); NodePointer demangleSymbolicReference(unsigned char rawKind, const void *at); diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index fdd50cb0c08ab..eaff74cfce578 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -257,6 +257,23 @@ class Compilation { /// limit filelists will be used. size_t FilelistThreshold; + /// Because each frontend job outputs the same info in its .d file, only do it + /// on the first job that actually runs. Write out dummies for the rest of the + /// jobs. This hack saves a lot of time in the build system when incrementally + /// building a project with many files. Record if a scheduled job has already + /// added -emit-dependency-path. + bool HaveAlreadyAddedDependencyPath = false; + +public: + /// When set, only the first scheduled frontend job gets the argument needed + /// to produce a make-style dependency file. The other jobs create dummy files + /// in the driver. This hack speeds up incremental compilation by reducing the + /// time for the build system to read each dependency file, which are all + /// identical. This optimization can be disabled by passing + /// -disable-only-one-dependency-file on the command line. + const bool OnlyOneDependencyFile; + +private: /// Scaffolding to permit experimentation with finer-grained dependencies and /// faster rebuilds. const bool EnableFineGrainedDependencies; @@ -309,6 +326,7 @@ class Compilation { bool SaveTemps = false, bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, + bool OnlyOneDependencyFile = false, bool EnableFineGrainedDependencies = false, bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, @@ -427,6 +445,16 @@ class Compilation { return FilelistThreshold; } + /// Since every make-style dependency file contains + /// the same information, incremental builds are sped up by only emitting one + /// of those files. Since the build system expects to see the files existing, + /// create dummy files for those jobs that don't emit real dependencies. + /// \param path The dependency file path + /// \param addDependencyPath A function to add an -emit-dependency-path + /// argument + void addDependencyPathOrCreateDummy(StringRef path, + function_ref addDependencyPath); + UnifiedStatsReporter *getStatsReporter() const { return Stats.get(); } diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index b72397379bb68..d927f3908a14f 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -158,7 +158,8 @@ class Driver { Interactive, // swift Batch, // swiftc AutolinkExtract, // swift-autolink-extract - SwiftIndent // swift-indent + SwiftIndent, // swift-indent + SymbolGraph // swift-symbolgraph }; class InputInfoMap; diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index 4627d4651e591..d36d3f3b3ff2d 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -194,6 +194,11 @@ class CommandOutput { /// first primary input. StringRef getAdditionalOutputForType(file_types::ID type) const; + /// Assuming (and asserting) that there are one or more input pairs, return true if there exists + /// an _additional_ (not primary) output of type \p type associated with the + /// first primary input. + bool hasAdditionalOutputForType(file_types::ID type) const; + /// Return a vector of additional (not primary) outputs of type \p type /// associated with the primary inputs. /// diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 129abe163b29f..a77186a52cba8 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -318,20 +318,11 @@ class CompilerInvocation { return CodeCompletionOffset != ~0U; } - void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) { - CodeCompletionFactory = Factory; - disableASTScopeLookup(); - } - /// Called from lldb, see rdar://53971116 void disableASTScopeLookup() { LangOpts.EnableASTScopeLookup = false; } - CodeCompletionCallbacksFactory *getCodeCompletionFactory() const { - return CodeCompletionFactory; - } - /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built, for use /// in generating a cached PCH file for the bridging header. @@ -385,6 +376,8 @@ class CompilerInvocation { /// fail an assert if not in that mode. std::string getModuleInterfaceOutputPathForWholeModule() const; + std::string getLdAddCFileOutputPathForWholeModule() const; + SerializationOptions computeSerializationOptions(const SupplementaryOutputPaths &outs, bool moduleIsPublic); @@ -481,6 +474,10 @@ class CompilerInstance { Diagnostics.addConsumer(*DC); } + void removeDiagnosticConsumer(DiagnosticConsumer *DC) { + Diagnostics.removeConsumer(*DC); + } + void createDependencyTracker(bool TrackSystemDeps) { assert(!Context && "must be called before setup()"); DepTracker = llvm::make_unique(TrackSystemDeps); @@ -545,6 +542,18 @@ class CompilerInstance { /// Returns true if there was an error during setup. bool setup(const CompilerInvocation &Invocation); + const CompilerInvocation &getInvocation() { + return Invocation; + } + + bool hasPersistentParserState() const { + return bool(PersistentState); + } + + PersistentParserState &getPersistentParserState() { + return *PersistentState.get(); + } + private: /// Set up the file system by loading and validating all VFS overlay YAML /// files. If the process of validating VFS files failed, or the overlay @@ -598,14 +607,13 @@ class CompilerInstance { void performSema(); /// Parses the input file but does no type-checking or module imports. - /// Note that this only supports parsing an invocation with a single file. void performParseOnly(bool EvaluateConditionals = false, - bool ParseDelayedBodyOnEnd = false); + bool CanDelayBodies = true); /// Parses and performs name binding on all input files. /// - /// Like a parse-only invocation, a single file is required. Unlike a - /// parse-only invocation, module imports will be processed. + /// This is similar to a parse-only invocation, but module imports will also + /// be processed. void performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 7a3e54ee8bc64..4289150c14554 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -31,6 +31,10 @@ struct ModuleInterfaceOptions { /// interface, or should we fully-qualify them? bool PreserveTypesAsWritten = false; + /// Should we emit the cType when printing @convention(c) or no? + /// FIXME: [clang-function-type-serialization] This check should go away. + bool PrintFullConvention = false; + /// Copy of all the command-line flags passed at .swiftinterface /// generation time, re-applied to CompilerInvocation when reading /// back .swiftinterface and reconstructing .swiftmodule. diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h new file mode 100644 index 0000000000000..0eb0de5eeb005 --- /dev/null +++ b/include/swift/IDE/CompletionInstance.h @@ -0,0 +1,93 @@ +//===--- CompletionInstance.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_COMPLETIONINSTANCE_H +#define SWIFT_IDE_COMPLETIONINSTANCE_H + +#include "swift/Frontend/Frontend.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace swift { + +class CompilerInstance; +class CompilerInvocation; +class DiagnosticConsumer; + +namespace ide { + +/// Copy a memory buffer inserting '\0' at the position of \c origBuf. +std::unique_ptr +makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + llvm::StringRef bufferIdentifier); + +/// Manages \c CompilerInstance for completion like operations. +class CompletionInstance { + unsigned MaxASTReuseCount = 100; + + std::mutex mtx; + + std::unique_ptr CachedCI; + llvm::hash_code CachedArgHash; + unsigned CachedReuseCount = 0; + + /// Calls \p Callback with cached \c CompilerInstance if it's usable for the + /// specified completion request. + /// Returns \c if the callback was called. Returns \c false if the compiler + /// argument has changed, primary file is not the same, the \c Offset is not + /// in function bodies, or the interface hash of the file has changed. + bool performCachedOperaitonIfPossible( + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + llvm::function_ref Callback); + + /// Calls \p Callback with new \c CompilerInstance for the completion + /// request. The \c CompilerInstace passed to the callback already performed + /// the first pass. + /// Returns \c false if it fails to setup the \c CompilerInstance. + bool performNewOperation( + llvm::Optional ArgsHash, + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); + +public: + /// Calls \p Callback with a \c CompilerInstance which is prepared for the + /// second pass. \p Callback is resposible to perform the second pass on it. + /// The \c CompilerInstance may be reused from the previous completions, + /// and may be cached for the next completion. + /// Return \c true if \p is successfully called, \c it fails. In failure + /// cases \p Error is populated with an error message. + /// + /// NOTE: \p Args is only used for checking the equaity of the invocation. + /// Since this function assumes that it is already normalized, exact the same + /// arguments including their order is considered as the same invocation. + bool performOperation( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_COMPLETIONINSTANCE_H diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index b1566ce894dff..d00a337f4eb21 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage) KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry) KEY_BOOL(IsABIPlaceholder, isABIPlaceholder) KEY_BOOL(IsExternal, isExternal) +KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers) +KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers) KEY(kind) diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index 84daad8bc9b86..b6565439a2016 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -70,6 +70,8 @@ RANGE_REFACTORING(ConvertIfLetExprToGuardExpr, "Convert To Guard Expression", co RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", convert.to.iflet.expr) +RANGE_REFACTORING(ConvertToComputedProperty, "Convert To Computed Property", convert.to.computed.property) + // These internal refactorings are designed to be helpful for working on // the compiler/standard library, etc., but are likely to be just confusing and // noise for general development. diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index d0a216a872405..89936350ed971 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -240,7 +240,7 @@ class NameMatcher: public ASTWalker { /// The \c Expr argument of a parent \c CustomAttr (if one exists) and /// the \c SourceLoc of the type name it applies to. - llvm::Optional> CustomAttrArg; + llvm::Optional> CustomAttrArg; unsigned InactiveConfigRegionNestings = 0; unsigned SelectorNestings = 0; @@ -584,6 +584,11 @@ ClangNode getEffectiveClangNode(const Decl *decl); /// Retrieve the Clang node for the given extension, if it has one. ClangNode extensionGetClangNode(const ExtensionDecl *ext); +/// Utility for finding the referenced declaration from a call, which might +/// include a second level of function application for a 'self.' expression, +/// or a curry thunk, etc. +std::pair getReferencedDecl(Expr *expr); + } // namespace ide } // namespace swift diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index f7cd87e7f063d..eeaabd8bbcbd9 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -65,7 +65,7 @@ class UniversalLinkageInfo { bool shouldAllPrivateDeclsBeVisibleFromOtherFiles() const { return HasMultipleIGMs; } - /// In case of multipe llvm modules, private lazy protocol + /// In case of multiple llvm modules, private lazy protocol /// witness table accessors could be emitted by two different IGMs during /// IRGen into different object files and the linker would complain about /// duplicate symbols. @@ -1157,6 +1157,11 @@ class ApplyIRLinkage { // TODO: BFD and gold do not handle COMDATs properly if (Triple.isOSBinFormatELF()) return; + // WebAssembly: hack: comdat + custom section = explosion + // the comdat code assumes section name would be unique for each comdat + // this doesn't happen for metadata. + if (Triple.isOSBinFormatWasm()) + return; if (IRL.Linkage == llvm::GlobalValue::LinkOnceODRLinkage || IRL.Linkage == llvm::GlobalValue::WeakODRLinkage) diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index d3ea9867b1bc2..fa5efdedc6be4 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -37,10 +37,10 @@ struct CommentParts { Optional Brief; ArrayRef BodyNodes; ArrayRef ParamFields; - Optional ReturnsField; - Optional ThrowsField; + Optional ReturnsField; + Optional ThrowsField; llvm::SmallSetVector Tags; - Optional LocalizationKeyField; + Optional LocalizationKeyField; bool isEmpty() const { return !Brief.hasValue() && diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 4196942bc66c6..7206520f310af 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -246,8 +246,10 @@ def debug_forbid_typecheck_prefix : Separate<["-"], "debug-forbid-typecheck-pref def debug_cycles : Flag<["-"], "debug-cycles">, HelpText<"Print out debug dumps when cycles are detected in evaluation">; +def build_request_dependency_graph : Flag<["-"], "build-request-dependency-graph">, + HelpText<"Build request dependency graph">; def output_request_graphviz : Separate<["-"], "output-request-graphviz">, - HelpText<"Emit GraphViz output visualizing the request graph">; + HelpText<"Emit GraphViz output visualizing the request dependency graph">; def debug_time_compilation : Flag<["-"], "debug-time-compilation">, HelpText<"Prints the time taken by each compilation phase">; @@ -268,6 +270,10 @@ def debug_crash_after_parse : Flag<["-"], "debug-crash-after-parse">, def debugger_support : Flag<["-"], "debugger-support">, HelpText<"Process swift code as if running in the debugger">; +def disable_clangimporter_source_import : Flag<["-"], + "disable-clangimporter-source-import">, + HelpText<"Disable ClangImporter and forward all requests straight the DWARF importer.">; + def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, @@ -598,6 +604,11 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; +def experimental_print_full_convention : + Flag<["-"], "experimental-print-full-convention">, + HelpText<"When emitting a module interface, emit additional @convention " + "arguments, regardless of whether they were written in the source">; + def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; @@ -633,6 +644,10 @@ def disable_verify_exclusivity : Flag<["-"], "disable-verify-exclusivity">, def disable_legacy_type_info : Flag<["-"], "disable-legacy-type-info">, HelpText<"Completely disable legacy type layout">; +def prespecialize_generic_metadata : Flag<["-"], "prespecialize-generic-metadata">, + HelpText<"Statically specialize metadata for generic types at types that " + "are known to be used in source.">; + def read_legacy_type_info_path_EQ : Joined<["-"], "read-legacy-type-info-path=">, HelpText<"Read legacy type layout from the given path instead of default path">; @@ -640,4 +655,7 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">, Flags<[FrontendOption]>, HelpText<"One of 'all', 'resilient' or 'fragile'">; +def emit_ldadd_cfile_path + : Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"">, + HelpText<"Generate .c file defining symbols to add back">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 71a01084e7945..bdb5a2e9e362c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -139,6 +139,16 @@ def enable_fine_grained_dependencies : Flag<["-"], "enable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, HelpText<"Experimental work-in-progress to be more selective about incremental recompilation">; + +def enable_only_one_dependency_file : +Flag<["-"], "enable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Enables incremental build optimization that only produces one dependencies file">; + +def disable_only_one_dependency_file : +Flag<["-"], "disable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Disables incremental build optimization that only produces one dependencies file">; + + def enable_source_range_dependencies : Flag<["-"], "enable-source-range-dependencies">, Flags<[]>, HelpText<"Try using source range information">; diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index cf18c459c642e..7b1c86308143e 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -118,10 +118,6 @@ class CodeCompletionCallbacks { /// Set target decl for attribute if the CC token is in attribute of the decl. virtual void setAttrTargetDeclKind(Optional DK) {} - /// Complete the whole expression. This is a fallback that should - /// produce results when more specific completion methods failed. - virtual void completeExpr() {}; - /// Complete expr-dot after we have consumed the dot. virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) {}; @@ -194,7 +190,7 @@ class CodeCompletionCallbacks { /// Complete the import decl with importable modules. virtual void - completeImportDecl(std::vector> &Path) {}; + completeImportDecl(std::vector> &Path) {}; /// Complete unresolved members after dot. virtual void completeUnresolvedMember(CodeCompletionExpr *E, diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 8441a12dc17d4..8cd9b75a415c7 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -293,7 +293,17 @@ class Lexer { /// resides. /// /// \param Loc The source location of the beginning of a token. - static Token getTokenAtLocation(const SourceManager &SM, SourceLoc Loc); + /// + /// \param CRM How comments should be treated by the lexer. Default is to + /// return the comments as tokens. This is needed in situations where + /// detecting the next semantically meaningful token is required, such as + /// the 'implicit self' diagnostic determining whether a capture list is + /// empty (i.e., the opening bracket is immediately followed by a closing + /// bracket, possibly with comments in between) in order to insert the + /// appropriate fix-it. + static Token getTokenAtLocation( + const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM = CommentRetentionMode::ReturnAsTokens); /// Retrieve the source location that points just past the diff --git a/include/swift/Parse/LocalContext.h b/include/swift/Parse/LocalContext.h index 8e4b2b281f5a0..1c02f0dfdc9b5 100644 --- a/include/swift/Parse/LocalContext.h +++ b/include/swift/Parse/LocalContext.h @@ -19,6 +19,7 @@ #define SWIFT_PARSE_LOCALCONTEXT_H #include "llvm/ADT/DenseMap.h" +#include "swift/AST/Identifier.h" #include namespace swift { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 9974e8212154c..93dfc1cfe3f6c 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -120,7 +120,7 @@ class Parser { DeclContext *CurDeclContext; ASTContext &Context; CodeCompletionCallbacks *CodeCompletion = nullptr; - std::vector>> AnonClosureVars; + std::vector>> AnonClosureVars; bool IsParsingInterfaceTokens = false; @@ -856,11 +856,19 @@ class Parser { void consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD); + ParserStatus parseBraceItems(SmallVectorImpl &Decls, + BraceItemListKind Kind, + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard); ParserStatus parseBraceItems(SmallVectorImpl &Decls, BraceItemListKind Kind = BraceItemListKind::Brace, BraceItemListKind ConditionalBlockKind = - BraceItemListKind::Brace); + BraceItemListKind::Brace) { + bool IsFollowingGuard = false; + return parseBraceItems(Decls, Kind, ConditionalBlockKind, + IsFollowingGuard); + } ParserResult parseBraceItemList(Diag<> ID); //===--------------------------------------------------------------------===// @@ -869,7 +877,7 @@ class Parser { /// Return true if parser is at the start of a decl or decl-import. bool isStartOfDecl(); - bool parseTopLevel(); + void parseTopLevel(); /// Flags that control the parsing of declarations. enum ParseDeclFlags { @@ -994,14 +1002,23 @@ class Parser { Optional &vjpSpec, TrailingWhereClause *&whereClause); - /// Parse a differentiation parameters clause. - bool parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName); + /// Parse a differentiability parameters clause, i.e. the 'wrt:' clause in + /// `@differentiable`, `@derivative`, and `@transpose` attributes. + /// + /// If `allowNamedParameters` is false, allow only index parameters and + /// 'self'. Used for `@transpose` attributes. + bool parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, + bool allowNamedParameters = true); /// Parse the @derivative attribute. ParserResult parseDerivativeAttribute(SourceLoc AtLoc, SourceLoc Loc); + /// Parse the @transpose attribute. + ParserResult parseTransposeAttribute(SourceLoc AtLoc, + SourceLoc Loc); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc); @@ -1143,7 +1160,19 @@ class Parser { SourceLoc &LAngleLoc, SourceLoc &RAngleLoc); - ParserResult parseTypeIdentifier(); + /// Parses a type identifier (e.g. 'Foo' or 'Foo.Bar.Baz'). + /// + /// When `isParsingQualifiedDeclBaseType` is true: + /// - Parses and returns the base type for a qualified declaration name, + /// positioning the parser at the '.' before the final declaration name. + // This position is important for parsing final declaration names like + // '.init' via `parseUnqualifiedDeclName`. + /// - For example, 'Foo.Bar.f' parses as 'Foo.Bar' and the parser is + /// positioned at '.f'. + /// - If there is no base type qualifier (e.g. when parsing just 'f'), returns + /// an empty parser error. + ParserResult parseTypeIdentifier( + bool isParsingQualifiedDeclBaseType = false); ParserResult parseOldStyleProtocolComposition(); ParserResult parseAnyType(); ParserResult parseSILBoxType(GenericParamList *generics, @@ -1357,6 +1386,14 @@ class Parser { bool canParseAsGenericArgumentList(); bool canParseType(); + + /// Returns true if a simple type identifier can be parsed. + /// + /// \verbatim + /// simple-type-identifier: identifier generic-argument-list? + /// \endverbatim + bool canParseSimpleTypeIdentifier(); + bool canParseTypeIdentifier(); bool canParseTypeIdentifierOrTypeComposition(); bool canParseOldStyleProtocolComposition(); @@ -1366,6 +1403,13 @@ class Parser { bool canParseTypedPattern(); + /// Returns true if a qualified declaration name base type can be parsed. + /// + /// \verbatim + /// qualified-decl-name-base-type: simple-type-identifier '.' + /// \endverbatim + bool canParseBaseTypeForQualifiedDeclName(); + //===--------------------------------------------------------------------===// // Expression Parsing ParserResult parseExpr(Diag<> ID) { @@ -1412,38 +1456,43 @@ class Parser { /// \param loc The location of the label (empty if it doesn't exist) void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc); - /// Parse an unqualified-decl-base-name. + enum class DeclNameFlag : uint8_t { + /// If passed, operator basenames are allowed. + AllowOperators = 1 << 0, + + /// If passed, names that coincide with keywords are allowed. Used after a + /// dot to enable things like '.init' and '.default'. + AllowKeywords = 1 << 1, + + /// If passed, 'deinit' and 'subscript' should be parsed as special names, + /// not ordinary identifiers. + AllowKeywordsUsingSpecialNames = AllowKeywords | 1 << 2, + + /// If passed, compound names with argument lists are allowed, unless they + /// have empty argument lists. + AllowCompoundNames = 1 << 4, + + /// If passed, compound names with empty argument lists are allowed. + AllowZeroArgCompoundNames = AllowCompoundNames | 1 << 5, + }; + using DeclNameOptions = OptionSet; + + friend DeclNameOptions operator|(DeclNameFlag flag1, DeclNameFlag flag2) { + return DeclNameOptions(flag1) | flag2; + } + + /// Without \c DeclNameFlag::AllowCompoundNames, parse an + /// unqualified-decl-base-name. /// - /// unqualified-decl-name: - /// identifier + /// unqualified-decl-base-name: identifier /// - /// \param afterDot Whether this identifier is coming after a period, which - /// enables '.init' and '.default' like expressions. - /// \param loc Will be populated with the location of the name. - /// \param diag The diagnostic to emit if this is not a name. - /// \param allowOperators Whether to allow operator basenames too. - DeclNameRef parseUnqualifiedDeclBaseName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators=false, - bool allowDeinitAndSubscript=false); - - /// Parse an unqualified-decl-name. + /// With \c DeclNameFlag::AllowCompoundNames, parse an unqualified-base-name. /// /// unqualified-decl-name: /// unqualified-decl-base-name /// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')' - /// - /// \param afterDot Whether this identifier is coming after a period, which - /// enables '.init' and '.default' like expressions. - /// \param loc Will be populated with the location of the name. - /// \param diag The diagnostic to emit if this is not a name. - /// \param allowOperators Whether to allow operator basenames too. - /// \param allowZeroArgCompoundNames Whether to allow empty argument lists. - DeclNameRef parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators=false, - bool allowZeroArgCompoundNames=false, - bool allowDeinitAndSubscript=false); + DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag, + DeclNameOptions flags); Expr *parseExprIdentifier(); Expr *parseExprEditorPlaceholder(Token PlaceholderTok, @@ -1474,6 +1523,8 @@ class Parser { /// identifier (',' identifier)* func-signature-result? 'in' /// \endverbatim /// + /// \param bracketRange The range of the brackets enclosing a capture list, if + /// present. Needed to offer fix-its for inserting 'self' into a capture list. /// \param captureList The entries in the capture list. /// \param params The parsed parameter list, or null if none was provided. /// \param arrowLoc The location of the arrow, if present. @@ -1482,12 +1533,14 @@ class Parser { /// /// \returns true if an error occurred, false otherwise. bool parseClosureSignatureIfPresent( - SmallVectorImpl &captureList, - ParameterList *¶ms, - SourceLoc &throwsLoc, - SourceLoc &arrowLoc, - TypeRepr *&explicitResultType, - SourceLoc &inLoc); + SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfParamDecl, + ParameterList *¶ms, + SourceLoc &throwsLoc, + SourceLoc &arrowLoc, + TypeRepr *&explicitResultType, + SourceLoc &inLoc); Expr *parseExprAnonClosureArg(); ParserResult parseExprList(tok LeftTok, tok RightTok, @@ -1602,12 +1655,8 @@ class Parser { //===--------------------------------------------------------------------===// // Code completion second pass. - static void - performCodeCompletionSecondPass(PersistentParserState &ParserState, - CodeCompletionCallbacksFactory &Factory); - void performCodeCompletionSecondPassImpl( - PersistentParserState::CodeCompletionDelayedDeclState &info); + CodeCompletionDelayedDeclState &info); }; /// Describes a parsed declaration name. diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 44f53983c6ca6..9e0678c5e3971 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -29,6 +29,33 @@ class SourceFile; class DeclContext; class IterableDeclContext; +enum class CodeCompletionDelayedDeclKind { + TopLevelCodeDecl, + Decl, + FunctionBody, +}; + +class CodeCompletionDelayedDeclState { +public: + CodeCompletionDelayedDeclKind Kind; + unsigned Flags; + DeclContext *ParentContext; + SavedScope Scope; + unsigned StartOffset; + unsigned EndOffset; + unsigned PrevOffset; + + SavedScope takeScope() { return std::move(Scope); } + + CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, + SavedScope &&Scope, unsigned StartOffset, + unsigned EndOffset, unsigned PrevOffset) + : Kind(Kind), Flags(Flags), ParentContext(ParentContext), + Scope(std::move(Scope)), StartOffset(StartOffset), EndOffset(EndOffset), + PrevOffset(PrevOffset) {} +}; + /// Parser state persistent across multiple parses. class PersistentParserState { public: @@ -39,50 +66,18 @@ class PersistentParserState { bool isValid() const { return Loc.isValid(); } }; - enum class CodeCompletionDelayedDeclKind { - TopLevelCodeDecl, - Decl, - FunctionBody, - }; - - class CodeCompletionDelayedDeclState { - friend class PersistentParserState; - friend class Parser; - CodeCompletionDelayedDeclKind Kind; - unsigned Flags; - DeclContext *ParentContext; - ParserPos BodyPos; - SourceLoc BodyEnd; - SavedScope Scope; - - SavedScope takeScope() { - return std::move(Scope); - } - - public: - CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, - unsigned Flags, DeclContext *ParentContext, - SourceRange BodyRange, SourceLoc PreviousLoc, - SavedScope &&Scope) - : Kind(Kind), Flags(Flags), - ParentContext(ParentContext), BodyPos{BodyRange.Start, PreviousLoc}, - BodyEnd(BodyRange.End), Scope(std::move(Scope)) {} - }; - bool InPoundLineEnvironment = false; // FIXME: When condition evaluation moves to a later phase, remove this bit // and adjust the client call 'performParseOnly'. bool PerformConditionEvaluation = true; private: - ScopeInfo ScopeInfo; + swift::ScopeInfo ScopeInfo; /// Parser sets this if it stopped parsing before the buffer ended. ParserPosition MarkedPos; std::unique_ptr CodeCompletionDelayedDeclStat; - std::vector DelayedDeclLists; - /// The local context for all top-level code. TopLevelContext TopLevelCode; @@ -92,26 +87,29 @@ class PersistentParserState { PersistentParserState(ASTContext &ctx) : PersistentParserState() { } ~PersistentParserState(); - void setCodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + void setCodeCompletionDelayedDeclState(SourceManager &SM, unsigned BufferID, + CodeCompletionDelayedDeclKind Kind, unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc); + void restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other); bool hasCodeCompletionDelayedDeclState() { return CodeCompletionDelayedDeclStat.get() != nullptr; } + CodeCompletionDelayedDeclState &getCodeCompletionDelayedDeclState() { + return *CodeCompletionDelayedDeclStat.get(); + } + std::unique_ptr takeCodeCompletionDelayedDeclState() { assert(hasCodeCompletionDelayedDeclState()); return std::move(CodeCompletionDelayedDeclStat); } - void delayDeclList(IterableDeclContext *D); - - void parseAllDelayedDeclLists(); - TopLevelContext &getTopLevelContext() { return TopLevelCode; } diff --git a/include/swift/Parse/Scope.h b/include/swift/Parse/Scope.h index 4fe00b028bef2..c10b97f35daee 100644 --- a/include/swift/Parse/Scope.h +++ b/include/swift/Parse/Scope.h @@ -141,6 +141,8 @@ class Scope { bool isResolvable() const; public: + Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock = false); + /// Create a lexical scope of the specified kind. Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock = false); diff --git a/include/swift/Reflection/Records.h b/include/swift/Reflection/Records.h index 431877b309420..c43ade467c6b0 100644 --- a/include/swift/Reflection/Records.h +++ b/include/swift/Reflection/Records.h @@ -266,6 +266,8 @@ struct AssociatedTypeRecordIterator { return *this; } + AssociatedTypeRecordIterator(const AssociatedTypeRecordIterator &Other) + : Cur(Other.Cur), End(Other.End) {} AssociatedTypeRecordIterator operator=(const AssociatedTypeRecordIterator &Other) { return { Other.Cur, Other.End }; diff --git a/include/swift/Runtime/ExistentialContainer.h b/include/swift/Runtime/ExistentialContainer.h index 7e1d733395790..bf7e63c7cb825 100644 --- a/include/swift/Runtime/ExistentialContainer.h +++ b/include/swift/Runtime/ExistentialContainer.h @@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl { using ClassExistentialContainer = ClassExistentialContainerImpl; using WeakClassExistentialContainer = ClassExistentialContainerImpl; +using UnownedClassExistentialContainer = + ClassExistentialContainerImpl; } // end swift namespace diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index 1b320e9d22a6e..a24c555ccca03 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -24,6 +24,8 @@ #include "swift/Runtime/MutexPThread.h" #elif defined(_WIN32) #include "swift/Runtime/MutexWin32.h" +#elif defined(__wasi__) +#include "swift/Runtime/MutexWASI.h" #else #error "Implement equivalent of MutexPThread.h/cpp for your platform." #endif diff --git a/include/swift/Runtime/MutexPThread.h b/include/swift/Runtime/MutexPThread.h index 1b06a2977bd06..50eef549cc0be 100644 --- a/include/swift/Runtime/MutexPThread.h +++ b/include/swift/Runtime/MutexPThread.h @@ -26,7 +26,7 @@ typedef pthread_cond_t ConditionHandle; typedef pthread_mutex_t MutexHandle; typedef pthread_rwlock_t ReadWriteLockHandle; -#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) || defined(__wasi__) // At the moment CYGWIN pthreads implementation doesn't support the use of // constexpr for static allocation versions. The way they define things // results in a reinterpret_cast which violates constexpr. Similarly, Android's diff --git a/include/swift/Runtime/MutexWASI.h b/include/swift/Runtime/MutexWASI.h new file mode 100644 index 0000000000000..28f212d6f48e9 --- /dev/null +++ b/include/swift/Runtime/MutexWASI.h @@ -0,0 +1,66 @@ +//===--- MutexWin32.h - -----------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Mutex, ConditionVariable, Read/Write lock, and Scoped lock implementations +// using Windows Slim Reader/Writer Locks and Conditional Variables. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_MUTEX_WASI_H +#define SWIFT_RUNTIME_MUTEX_WASI_H + +namespace swift { + +typedef void* ConditionHandle; +typedef void* MutexHandle; +typedef void* ReadWriteLockHandle; + +#define SWIFT_CONDITION_SUPPORTS_CONSTEXPR 1 +#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1 +#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1 + +struct ConditionPlatformHelper { + static constexpr ConditionHandle staticInit() { + return nullptr; + }; + static void init(ConditionHandle &condition) {} + static void destroy(ConditionHandle &condition) {} + static void notifyOne(ConditionHandle &condition) {} + static void notifyAll(ConditionHandle &condition) {} + static void wait(ConditionHandle &condition, MutexHandle &mutex); +}; + +struct MutexPlatformHelper { + static constexpr MutexHandle staticInit() { return nullptr; } + static void init(MutexHandle &mutex, bool checked = false) {} + static void destroy(MutexHandle &mutex) {} + static void lock(MutexHandle &mutex) {} + static void unlock(MutexHandle &mutex) {} + static bool try_lock(MutexHandle &mutex) { return true; } + static void unsafeLock(MutexHandle &mutex) {} + static void unsafeUnlock(MutexHandle &mutex) {} +}; + +struct ReadWriteLockPlatformHelper { + static constexpr ReadWriteLockHandle staticInit() { return nullptr; } + static void init(ReadWriteLockHandle &rwlock) {} + static void destroy(ReadWriteLockHandle &rwlock) {} + static void readLock(ReadWriteLockHandle &rwlock) {} + static bool try_readLock(ReadWriteLockHandle &rwlock) { return true; } + static void readUnlock(ReadWriteLockHandle &rwlock) {} + static void writeLock(ReadWriteLockHandle &rwlock) {} + static bool try_writeLock(ReadWriteLockHandle &rwlock) { return true; } + static void writeUnlock(ReadWriteLockHandle &rwlock) {} +}; +} + +#endif diff --git a/include/swift/Runtime/Once.h b/include/swift/Runtime/Once.h index 8a78cddc23c56..95265cfcda794 100644 --- a/include/swift/Runtime/Once.h +++ b/include/swift/Runtime/Once.h @@ -44,6 +44,10 @@ typedef std::once_flag swift_once_t; /// extent of type swift_once_t. SWIFT_RUNTIME_EXPORT void swift_once(swift_once_t *predicate, void (*fn)(void *), void *context); +#ifdef __wasm__ +// WebAssembly: hack +void swift_once_real(swift_once_t *predicate, void (*fn)(void *), void *context); +#endif } diff --git a/include/swift/SIL/SILAllocated.h b/include/swift/SIL/SILAllocated.h index 4e967896ceec4..767ba21d7ada5 100644 --- a/include/swift/SIL/SILAllocated.h +++ b/include/swift/SIL/SILAllocated.h @@ -31,7 +31,7 @@ class SILAllocated { void *operator new[](size_t) = delete; /// Disable non-placement delete. - void operator delete(void *) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *) = delete; void operator delete[](void *) = delete; /// Custom version of 'new' that uses the SILModule's BumpPtrAllocator with diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index 109e50338a70b..4f23c1cd6ff46 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -89,7 +89,7 @@ class SILArgument : public ValueBase { public: void operator=(const SILArgument &) = delete; - void operator delete(void *, size_t) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *, size_t) = delete; ValueOwnershipKind getOwnershipKind() const { return static_cast(Bits.SILArgument.VOKind); diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 450e8aa19bb9f..dd5748bb24951 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -54,7 +54,7 @@ public llvm::ilist_node, public SILAllocated { SILBasicBlock() : Parent(nullptr) {} void operator=(const SILBasicBlock &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; SILBasicBlock(SILFunction *F, SILBasicBlock *relativeToBB, bool after); diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index cbfa005100669..bda95ab9f3aca 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1783,12 +1783,6 @@ class SILBuilder { // Unchecked cast helpers //===--------------------------------------------------------------------===// - // Create an UncheckedRefCast if the source and dest types are legal, - // otherwise return null. - // Unwrap or wrap optional types as needed. - SingleValueInstruction *tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy); - // Create the appropriate cast instruction based on result type. SingleValueInstruction *createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty); diff --git a/include/swift/SIL/SILConstants.h b/include/swift/SIL/SILConstants.h index a6e741a979485..2c35655ca8ab0 100644 --- a/include/swift/SIL/SILConstants.h +++ b/include/swift/SIL/SILConstants.h @@ -606,8 +606,8 @@ class SymbolicValue { static_assert(sizeof(SymbolicValue) == 2 * sizeof(uint64_t), "SymbolicValue should stay small"); -static_assert(std::is_pod::value, - "SymbolicValue should stay POD"); +static_assert(std::is_trivial::value, + "SymbolicValue should stay trivial"); inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, SymbolicValue val) { val.print(os); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5e72d5b1be14d..4afcb22beb988 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -42,6 +42,7 @@ #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" #include "llvm/Support/TrailingObjects.h" +#include namespace swift { @@ -318,7 +319,7 @@ class SILInstruction SILInstruction() = delete; void operator=(const SILInstruction &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; /// Check any special state of instructions that are not represented in the /// instructions operands/type. @@ -799,7 +800,7 @@ class SingleValueInstruction : public SILInstruction, public ValueBase { SILModule &getModule() const { return SILInstruction::getModule(); } SILInstructionKind getKind() const { return SILInstruction::getKind(); } - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; ValueKind getValueKind() const { return ValueBase::getKind(); @@ -950,7 +951,7 @@ class MultipleValueInstruction : public SILInstruction { : SILInstruction(kind, loc) {} public: - void operator delete(void *Ptr, size_t)SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Ptr, size_t) = delete; MultipleValueInstruction *clone(SILInstruction *insertPt = nullptr) { return cast(SILInstruction::clone(insertPt)); @@ -6998,13 +6999,13 @@ class TermInst : public NonValueInstruction { }); } - using SuccessorBlockArgumentsListTy = - TransformRange( const SILSuccessor &)>>; /// Return the range of Argument arrays for each successor of this /// block. - SuccessorBlockArgumentsListTy getSuccessorBlockArguments() const; + SuccessorBlockArgumentListTy getSuccessorBlockArgumentLists() const; using SuccessorBlockListTy = TransformRange { friend SILBuilder; - SILSuccessor DestBBs[2]; + std::array DestBBs; YieldInst(SILDebugLocation loc, ArrayRef yieldedValues, SILBasicBlock *normalBB, SILBasicBlock *unwindBB) : InstructionBaseWithTrailingOperands(yieldedValues, loc), - DestBBs{{this, normalBB}, {this, unwindBB}} {} + DestBBs{{{this, normalBB}, {this, unwindBB}}} {} static YieldInst *create(SILDebugLocation loc, ArrayRef yieldedValues, @@ -7253,7 +7254,8 @@ class CondBranchInst final FalseIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; + /// The number of arguments for the True branch. unsigned getNumTrueArgs() const { return SILInstruction::Bits.CondBranchInst.NumTrueArgs; @@ -7638,7 +7640,7 @@ class DynamicMethodBranchInst SILDeclRef Member; - SILSuccessor DestBBs[2]; + std::array DestBBs; /// The operand. FixedOperandList<1> Operands; @@ -7686,7 +7688,7 @@ class CheckedCastBranchInst final: CanType DestFormalTy; bool IsExact; - SILSuccessor DestBBs[2]; + std::array DestBBs; CheckedCastBranchInst(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, @@ -7698,8 +7700,8 @@ class CheckedCastBranchInst final: TypeDependentOperands), DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy), - IsExact(IsExact), DestBBs{{this, SuccessBB, Target1Count}, - {this, FailureBB, Target2Count}} {} + IsExact(IsExact), DestBBs{{{this, SuccessBB, Target1Count}, + {this, FailureBB, Target2Count}}} {} static CheckedCastBranchInst * create(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, @@ -7746,7 +7748,7 @@ class CheckedCastValueBranchInst final SILType DestLoweredTy; CanType DestFormalTy; - SILSuccessor DestBBs[2]; + std::array DestBBs; CheckedCastValueBranchInst(SILDebugLocation DebugLoc, SILValue Operand, CanType SourceFormalTy, @@ -7757,7 +7759,7 @@ class CheckedCastValueBranchInst final TypeDependentOperands), SourceFormalTy(SourceFormalTy), DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy), - DestBBs{{this, SuccessBB}, {this, FailureBB}} {} + DestBBs{{{this, SuccessBB}, {this, FailureBB}}} {} static CheckedCastValueBranchInst * create(SILDebugLocation DebugLoc, @@ -7791,7 +7793,7 @@ class CheckedCastAddrBranchInst CastConsumptionKind ConsumptionKind; FixedOperandList<2> Operands; - SILSuccessor DestBBs[2]; + std::array DestBBs; CanType SourceType; CanType TargetType; @@ -7803,8 +7805,8 @@ class CheckedCastAddrBranchInst ProfileCounter Target1Count, ProfileCounter Target2Count) : InstructionBase(DebugLoc), ConsumptionKind(consumptionKind), - Operands{this, src, dest}, DestBBs{{this, successBB, Target1Count}, - {this, failureBB, Target2Count}}, + Operands{this, src, dest}, DestBBs{{{this, successBB, Target1Count}, + {this, failureBB, Target2Count}}}, SourceType(srcType), TargetType(targetType) { assert(ConsumptionKind != CastConsumptionKind::BorrowAlways && "BorrowAlways is not supported on addresses"); @@ -7856,7 +7858,7 @@ class TryApplyInstBase : public TermInst { ErrorIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; protected: TryApplyInstBase(SILInstructionKind valueKind, SILDebugLocation Loc, diff --git a/include/swift/SIL/SILUndef.h b/include/swift/SIL/SILUndef.h index bb1c0edf41ad1..94036ff791682 100644 --- a/include/swift/SIL/SILUndef.h +++ b/include/swift/SIL/SILUndef.h @@ -29,7 +29,7 @@ class SILUndef : public ValueBase { public: void operator=(const SILArgument &) = delete; - void operator delete(void *, size_t) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *, size_t) = delete; static SILUndef *get(SILType ty, SILModule &m, ValueOwnershipKind ownershipKind); static SILUndef *get(SILType ty, const SILFunction &f); diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index 8fca7c4ee09a1..7662ae50c29db 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -586,6 +586,9 @@ class Operand { Operand(const Operand &use) = delete; Operand &operator=(const Operand &use) = delete; + Operand(Operand &&) = default; + Operand &operator=(Operand &&) = default; + /// Return the current value being used by this operand. SILValue get() const { return TheValue; } diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 2e2a06876687c..eeea1c847d8c6 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -946,24 +946,6 @@ class TypeConverter { CaptureInfo getLoweredLocalCaptures(SILDeclRef fn); bool hasLoweredLocalCaptures(SILDeclRef fn); -#ifndef NDEBUG - /// If \c false, \c childDC is in a context it cannot capture variables from, - /// so it is expected that Sema may not have computed its \c CaptureInfo. - /// - /// This call exists for use in assertions; do not use it to skip capture - /// processing. - static bool canCaptureFromParent(DeclContext *childDC) { - // This call was added because Sema leaves the captures of functions that - // cannot capture anything uncomputed. - // TODO: Make Sema set them to CaptureInfo::empty() instead. - - if (childDC) - if (auto decl = childDC->getAsDecl()) - return decl->getDeclContext()->isLocalContext(); - return true; - } -#endif - enum class ABIDifference : uint8_t { // Types have compatible calling conventions and representations, so can // be trivially bitcast. diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index fe0837c9549ba..7c743f9ec45b9 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -194,10 +194,10 @@ class AliasAnalysis : public SILAnalysis { return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias; } - /// \returns True if the release of the \p Ptr can access memory accessed by - /// \p User. + /// \returns True if the release of the \p releasedReference can access or + /// free memory accessed by \p User. bool mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr); + SILValue releasedReference); /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 9c4b364383d11..aa32d361535e5 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -43,18 +43,20 @@ /// return a /// /// Generates the following connection graph, where 'a' is in the SILValue %0: -/// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content -/// Con %0.1 Esc: G, Succ: // Represents the content of 'a' -/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' +/// Val [ref] %1 Esc: R, Succ: (%1) // Reference 'a'; points to 'a's object +/// Con [int] %2 Esc: R, Succ: (%2.1) // Object pointed to by 'a' +/// Con %2.1 Esc: G, Succ: // Fields in the object +/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' /// /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. /// These states form a lattice in which None is the most refined, or top, state /// and Global is the least refined, or bottom, state. Merging nodes performs a -/// meet operation on their escaping states. At a call site, the callee graph is -/// merged with the callee graph by merging the respective call argument -/// nodes. A node has a "Return" escaping state if it only escapes by being -/// returned from the current function. A node has an "Argument" escaping state -/// if only escapes by being passed as an incoming argument to this function. +/// meet operation on their escaping states, moving the state down in the +/// lattice. At a call site, the callee graph is merged with the caller graph by +/// merging the respective call argument nodes. A node has a "Return" escaping +/// state if it only escapes by being returned from the current function. A node +/// has an "Argument" escaping state if only escapes by being passed as an +/// incoming argument to this function. /// /// A directed edge between two connection graph nodes indicates that the memory /// represented by the destination node memory is reachable via an address @@ -81,30 +83,37 @@ /// /// Generates the following connection graph, where the alloc_stack for variable /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. -/// Val %0 Esc: G, Succ: (%0.1) -/// Con %0.1 Esc: G, Succ: %3 -/// Val %3 Esc: G, Succ: (%3.1) -/// Con %3.1 Esc: G, Succ: -/// Ret Esc: R, Succ: %3 +/// +/// Val %0 Esc: , Succ: (%0.1) // Stack address of 'a' +/// Con [ref] %0.1 Esc: , Succ: %3 // Local reference 'a', aliased with %3 +/// Val [ref] %3 Esc: , Succ: (%4) // Local instance 'a', stored in %0.1 +/// Con [int] %4 Esc: G, Succ: (%4.1) // Object, escapes +/// Con %4.1 Esc: G, Succ: // Fields, escapes +/// Ret Esc: , Succ: (%4), %3 /// /// The value node for variable 'a' now points to local variable storage /// (%0.1). That local variable storage contains a reference. Assignment into /// that reference creates a defer edge to the allocated reference (%3). The -/// allocated reference in turn points to the object storage (%3.1). +/// allocated reference in turn points to the object storage (%4). /// -/// Note that a variable holding a single class reference and a variable -/// holding a non-trivial struct has the same graph representation. The -/// variable's content node only represents the value of the references, not the -/// memory pointed-to by the reference. +/// Note that a variable holding a single class reference and a variable holding +/// a non-trivial struct will have the same graph representation. A '[ref]' flag +/// on a node indicates that the all pointer-type fields that may be stored +/// inside the memory represented by that node are references. This allows alias +/// analysis to assume the object the node points to will not be released when +/// the node's memory is released as long as there are subsequent accesses to +/// the object accessed via a different path in the connection graph. /// /// A pointsTo edge does not necessarily indicate pointer indirection. It may -/// simply represent a derived address within the same object. This allows -/// escape analysis to view an object's memory in layers, each with separate -/// escaping properties. For example, a class object's first-level content node -/// represents the object header including the metadata pointer and reference -/// count. An object's second level content node only represents the -/// reference-holding fields within that object. Consider the connection graph -/// for a class with properties: +/// simply represent a derived address within the same object. A node that +/// points to the same logical object must be flagged as an interior node +/// ('[int]'). Interior nodes always have pointsTo content representing the rest +/// of the object. This allows escape analysis to view an object's memory in +/// layers, each with separate escaping properties. For example, a class +/// object's first-level content node represents the object header including the +/// metadata pointer and reference count. An object's second level content node +/// only represents the reference-holding fields within that object. Consider +/// the connection graph for a class with properties: /// /// class HasObj { /// var obj: AnyObject @@ -114,35 +123,37 @@ /// } /// /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: -/// Arg %0 Esc: A, Succ: (%0.1) -/// Con %0.1 Esc: A, Succ: (%0.2) -/// Con %0.2 Esc: A, Succ: %1 -/// Arg %1 Esc: A, Succ: (%1.1) -/// Con %1.1 Esc: A, Succ: (%1.2) -/// Con %1.2 Esc: G, Succ: /// -/// Node %0.1 represents the header of 'h', including reference count and -/// metadata pointer. This node points to %0.2 which represents the 'obj' -/// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to -/// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any -/// potential nontrivial properties in 'o' which may have escaped globally when -/// 'o' was released. +/// Arg [ref] %0 Esc: A, Succ: (%6) // 'h' +/// Arg [ref] %1 Esc: A, Succ: (%1.1) // 'o' +/// Con [int] %1.1 Esc: A, Succ: (%1.2) // 'o' object +/// Con %1.2 Esc: A, Succ: (%1.3) // 'o' fields +/// Con %1.3 Esc: G, Succ: // memory 'h.obj' may point to +/// Con [int] %6 Esc: A, Succ: (%7) // 'h' object +/// Con %7 Esc: A, Succ: %1 // 'h.obj' +/// +/// Node %1.1 represents the header of 'o', including reference count and +/// metadata pointer. This node points to %1.2 which represents the 'obj' +/// property. '%1.3' represents any potential nontrivial properties in 'o' which +/// may have escaped globally when 'o' was released. '%6' is a ref_element_addr +/// accessing 'h.obj'. '%7' is a load of 'h.obj'. The assignment 'h.obj = o' +/// creates a defer edge from '%7' to '%1'. /// /// The connection graph is constructed by summarizing all memory operations in /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted /// connection graph. /// -/// In addition to the connection graph, EscapeAnalysis stores information about -/// "use points". Each release operation is a use points. These instructions are -/// recorded in a table and given an ID. Each connection graph node stores a -/// bitset indicating the use points reachable via the CFG by that node. This -/// provides some flow-sensitive information on top of the otherwise flow -/// insensitive connection graph. -/// -/// Note: storing bitsets in each node may be unnecessary overhead since the -/// same information can be obtained with a graph traversal, typically of only -/// 1-3 hops. -// ===---------------------------------------------------------------------===// +/// In addition to the connection graph, EscapeAnalysis caches information about +/// "use points". Each release operation in which the released reference can be +/// identified is a considered a use point. The use point instructions are +/// recorded in a table and given an ID. Each connection graph content node +/// stores a bitset indicating the use points that may release references that +/// point to by that content node. Correctness relies on an invariant: for each +/// reference-type value in the function, all points in the function which may +/// release the reference must be identified as use points of the node that the +/// value points to. If the reference-type value may be released any other +/// way, then its content node must be marked escaping. +/// ===---------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ @@ -234,6 +245,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Global }; + // Must be ordered from most precise to least precise. A meet across memory + // locations (such as aggregate fields) always moves down. + enum PointerKind { NoPointer, ReferenceOnly, AnyPointer }; + static bool canOnlyContainReferences(PointerKind pointerKind) { + return pointerKind <= ReferenceOnly; + } + public: class CGNode; class ConnectionGraph; @@ -320,25 +338,52 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// is completely unlinked from the graph, bool isMerged = false; - /// True if this is a content node that owns a reference count. Such a - /// content node necessarilly keeps alive all content it points to until it - /// is released. This can be conservatively false. - bool hasRC = false; + /// True if this node's pointsTo content is part of the same object with + /// respect to SIL memory access. When this is true, then this node must + /// have a content node. + /// + /// When this flag is false, it provides a "separate access guarantee" for + /// stronger analysis. If the base object for a SIL memory access is mapped + /// to this node, then the accessed memory must have the same escaping + /// properties as the base object. In contrast, when this is true, the + /// connection graph may model the escaping properties of the base object + /// separately from the accessed memory. + bool isInteriorFlag; + + /// True if this node can only point to other nodes via a reference, not an + /// address or raw pointer. + /// + /// When this flag is true, it provides a "separate lifetime guarantee". Any + /// reference modeled by this node keeps alive all pointsTo content until + /// it is released. e.g. Given two nodes that both pointTo the same + /// content, releasing the value associated one node cannot free that + /// content as long as the released node is reference-only and the value + /// associated with the other node is accessed later. + /// + /// Note that an object field may contain a raw pointer which could point + /// anywhere, even back into the same object. In that case + /// hasReferenceOnlyFlag must be false. + /// + /// Typically, when this is true, the node represents at least one + /// reference, however, a return node may be created even when the return + /// type is NoPointer. + bool hasReferenceOnlyFlag; /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type, bool hasRC) - : mappedValue(V), UsePoints(0), hasRC(hasRC), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool isInterior, bool hasReferenceOnly) + : mappedValue(V), UsePoints(0), isInteriorFlag(isInterior), + hasReferenceOnlyFlag(hasReferenceOnly), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: - assert(V); + assert(V && !isInteriorFlag); break; case NodeType::Return: - assert(!V); + assert(!V && !isInteriorFlag); break; case NodeType::Content: // A content node representing the returned value has no associated @@ -360,6 +405,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Changed; } + void mergeFlags(bool isInterior, bool hasReferenceOnly); + // Merge the properties of \p fromNode into this node. void mergeProperties(CGNode *fromNode); @@ -454,10 +501,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Return true if this node represents content. bool isContent() const { return Type == NodeType::Content; } - /// Return true if this node represents an entire reference counted object. - bool hasRefCount() const { return hasRC; } + /// Return true if this node pointsTo the same object. + bool isInterior() const { return isInteriorFlag; } - void setRefCount(bool rc) { hasRC = rc; } + void setInterior(bool isInterior) { isInteriorFlag = isInterior; } + + /// Return true if this node can only point to another node via a reference, + /// as opposed to an address or raw pointer. + bool hasReferenceOnly() const { return hasReferenceOnlyFlag; } + + void setHasReferenceOnly(bool hasReferenceOnly) { + hasReferenceOnlyFlag = hasReferenceOnly; + } /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -586,10 +641,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { CGNode *ReturnNode = nullptr; /// The list of use points. - llvm::SmallVector UsePointTable; + llvm::SmallVector UsePointTable; /// Mapping of use points to bit indices in CGNode::UsePoints. - llvm::DenseMap UsePoints; + llvm::DenseMap UsePoints; /// The allocator for nodes. llvm::SpecificBumpPtrAllocator NodeAllocator; @@ -614,11 +669,13 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Allocates a node of a given type. /// - /// hasRC is set for Content nodes based on the type and origin of - /// the pointer. - CGNode *allocNode(ValueBase *V, NodeType Type, bool hasRC = false) { - assert(Type == NodeType::Content || !hasRC); - CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type, hasRC); + /// isInterior is always false for non-content nodes and is set for content + /// nodes based on the type and origin of the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, + bool hasReferenceOnly) { + assert((Type == NodeType::Content) || !isInterior); + CGNode *Node = new (NodeAllocator.Allocate()) + CGNode(V, Type, isInterior, hasReferenceOnly); Nodes.push_back(Node); return Node; } @@ -665,6 +722,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } + // Helper for getNode and getValueContent. + CGNode *getOrCreateNode(ValueBase *V, PointerKind pointerKind); + /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is /// taken. This means the node is always created for the "outermost" value @@ -672,9 +732,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNode(ValueBase *V, bool createIfNeeded = true); - /// Helper to create and return a content node with the given \p hasRC - /// flag. \p addrNode will gain a points-to edge to the new content node. - CGNode *createContentNode(CGNode *addrNode, bool hasRC); + // Helper for getValueContent to create and return a content node with the + // given \p isInterior and \p hasReferenceOnly flags. \p addrNode + // will gain a points-to edge to the new content node. + CGNode *createContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); + + CGNode *getOrCreateContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); /// Create a new content node based on an existing content node to support /// graph merging. @@ -683,18 +748,33 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// state will be initialized based on the \p srcContent node. CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); - /// Get a node represnting the field data within the given RC node. - CGNode *getFieldContent(CGNode *rcNode); + // Helper for getValueContent to get the content node for an address, which + // may be variable content or field content. + CGNode *getOrCreateAddressContent(SILValue addrVal, CGNode *addrNode); + + // Helper for getValueContent to get the content representing a referenced + // object. \p refVal's type may or may not have reference semantics. The + // caller must knows based on the operand using \p refVal that it contains a + // reference. + CGNode *getOrCreateReferenceContent(SILValue refVal, CGNode *refNode); + + // Helper for getValueContent to get the content node for an unknown pointer + // value. This is useful to determine whether multiple nodes are in the same + // defer web, but is otherwise conservative. + CGNode *getOrCreateUnknownContent(CGNode *addrNode); + + /// Get a node representing the field data within the given object node. + /// If objNode was a recognized reference-only value, then it's content node + /// will already be initialized according to the reference type. Otherwise, + /// return null. + CGNode *getFieldContent(CGNode *objNode) { + if (!objNode->isInterior()) + return nullptr; + return objNode->getContentNodeOrNull(); + } /// Get or creates a pseudo node for the function return value. - CGNode *getReturnNode() { - if (!ReturnNode) { - ReturnNode = allocNode(nullptr, NodeType::Return); - if (!isSummaryGraph) - ReturnNode->mergeEscapeState(EscapeState::Return); - } - return ReturnNode; - } + CGNode *getReturnNode(); /// Returns the node for the function return value if present. CGNode *getReturnNodeOrNull() const { @@ -718,28 +798,25 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Node->mappedValue = V; } - /// Adds an argument/instruction in which the node's value is used. - int addUsePoint(CGNode *Node, SILNode *User) { - if (Node->getEscapeState() >= EscapeState::Global) - return -1; - - User = User->getRepresentativeSILNodeInObject(); - int Idx = (int)UsePoints.size(); - assert(UsePoints.count(User) == 0 && "value is already a use-point"); - UsePoints[User] = Idx; - UsePointTable.push_back(User); - assert(UsePoints.size() == UsePointTable.size()); - Node->setUsePointBit(Idx); - return Idx; + /// If \p pointer is a pointer, set it's content to global escaping. + /// + /// Only mark the content node as escaping. Marking a pointer node as + /// escaping would generally be meaningless because it may have aliases or + /// defer edges. Marking the pointer node as escaping would also be too + /// conservative because, when the pointer is mapped to a content node, it + /// would behave as if the memory containing the pointer also escaped. + /// + /// If the pointer is mapped to a node, then calling setEscapesGlobal must + /// ensure that it points to a content node. Even if we could mark the + /// pointer node as escaping, it would be insufficient because only content + /// nodes are merged into the caller graph. + void setEscapesGlobal(SILValue pointer) { + if (CGNode *content = getValueContent(pointer)) + content->markEscaping(); } - void escapeContentsOf(CGNode *Node) { - CGNode *escapedContent = Node->getContentNodeOrNull(); - if (!escapedContent) { - escapedContent = createContentNode(Node, /*hasRC=*/false); - } - escapedContent->markEscaping(); - } + /// Adds an argument/instruction in which the node's value is used. + int addUsePoint(CGNode *Node, SILInstruction *User); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). @@ -794,10 +871,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { template bool forwardTraverseDefer(CGNode *startNode, CGNodeVisitor &&visitor); - /// Return true if \p pointer may indirectly point to \pointee via pointers - /// and object references. - bool mayReach(CGNode *pointer, CGNode *pointee); - public: /// Gets or creates a node for a value \p V. /// If V is a projection(-path) then the base of the projection(-path) is @@ -806,6 +879,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNodeOrNull(ValueBase *V) { return getNode(V, false); } + /// Get the content node pointed to by \p ptrVal. + /// + /// If \p ptrVal cannot be mapped to a node, return nullptr. + /// + /// If \p ptrVal is mapped to a node, return a non-null node representing + /// the content that \p ptrVal points to. + CGNode *getValueContent(SILValue ptrVal); + /// Returns the number of use-points of a node. int getNumUsePoints(CGNode *Node) { assert(!Node->escapes() && @@ -817,11 +898,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// (indirectly) somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. - bool isUsePoint(SILNode *UsePoint, CGNode *Node); + bool isUsePoint(SILInstruction *UsePoint, CGNode *Node); /// Returns all use points of \p Node in \p UsePoints. void getUsePoints(CGNode *Node, - llvm::SmallVectorImpl &UsePoints); + llvm::SmallVectorImpl &UsePoints); /// Computes the use point information. void computeUsePoints(); @@ -901,6 +982,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { MaxGraphMerges = 4 }; + using PointerKindCache = llvm::DenseMap; + /// The connection graphs for all functions (does not include external /// functions). llvm::DenseMap Function2Info; @@ -909,7 +992,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; /// Cache for isPointer(). - llvm::DenseMap isPointerCache; + PointerKindCache pointerKindCache; SILModule *M; @@ -919,37 +1002,24 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Callee analysis, used for determining the callees at call sites. BasicCalleeAnalysis *BCA; - /// Returns true if \p V may encapsulate a "pointer" value. - /// See EscapeAnalysis::NodeType::Value. - bool isPointer(ValueBase *V) const; - /// If EscapeAnalysis should consider the given value to be a derived address /// or pointer based on one of its address or pointer operands, then return /// that operand value. Otherwise, return an invalid value. - SILValue getPointerBase(SILValue value) const; + SILValue getPointerBase(SILValue value); /// Recursively find the given value's pointer base. If the value cannot be /// represented in EscapeAnalysis as one of its operands, then return the same /// value. - SILValue getPointerRoot(SILValue value) const; + SILValue getPointerRoot(SILValue value); - /// If \p pointer is a pointer, set it to global escaping. - void setEscapesGlobal(ConnectionGraph *conGraph, ValueBase *pointer) { - CGNode *Node = conGraph->getNode(pointer); - if (!Node) - return; + PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; - if (Node->isContent()) { - Node->markEscaping(); - return; - } - Node->mergeEscapeState(EscapeState::Global); + PointerKind findCachedPointerKind(SILType Ty, const SILFunction &F) const; - // Make sure to have a content node. Otherwise we may end up not merging - // the global-escape state into a caller graph (only content nodes are - // merged). Either the node itself is a content node or we let the node - // point to one. - conGraph->escapeContentsOf(Node); + // Returns true if the type \p Ty must be a reference or must transitively + // contain a reference and no other pointer or address type. + bool hasReferenceOnly(SILType Ty, const SILFunction &F) const { + return findCachedPointerKind(Ty, F) <= ReferenceOnly; } /// Gets or creates FunctionEffects for \p F. @@ -960,15 +1030,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return FInfo; } - /// Get or create the node representing the memory pointed to by \p - /// addrVal. If \p addrVal is an address, then return the content node for the - /// variable's memory. Otherwise, \p addrVal may contain a reference, so - /// return the content node for the referenced heap object. - /// - /// Note that \p addrVal cannot be an address within a heap object, such as - /// an address from ref_element_addr or project_box. - CGNode *getValueContent(ConnectionGraph *conGraph, SILValue addrVal); - /// Build a connection graph for reach callee from the callee list. bool buildConnectionGraphForCallees(SILInstruction *Caller, CalleeList Callees, @@ -990,6 +1051,33 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); + // @_semantics("array.uninitialized") takes a reference to the storage and + // returns an instantiated array struct and unsafe pointer to the elements. + struct ArrayUninitCall { + SILValue arrayStorageRef; + TupleExtractInst *arrayStruct = nullptr; + TupleExtractInst *arrayElementPtr = nullptr; + + bool isValid() const { + return arrayStorageRef && arrayStruct && arrayElementPtr; + } + }; + + /// If \p ai is an optimizable @_semantics("array.uninitialized") call, return + /// valid call information. + ArrayUninitCall canOptimizeArrayUninitializedCall(ApplyInst *ai, + ConnectionGraph *conGraph); + + /// Return true of this tuple_extract is the result of an optimizable + /// @_semantics("array.uninitialized") call. + bool canOptimizeArrayUninitializedResult(TupleExtractInst *tei); + + /// Handle a call to "@_semantics(array.uninitialized") precisely by mapping + /// each call result to a separate graph node and relating them to the + /// argument. + void createArrayUninitializedSubgraph(ArrayUninitCall call, + ConnectionGraph *conGraph); + /// Updates the graph by analyzing instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. @@ -1022,10 +1110,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph); - /// Returns true if the value \p V can escape to the \p UsePoint, where - /// \p UsePoint is either a release-instruction or a function call. - bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph); + /// Returns true if the value \p value or any address within that value can + /// escape to the \p usePoint, where \p usePoint is either a + /// release-instruction or a function call. + bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint, + ConnectionGraph *conGraph); friend struct ::CGForDotView; @@ -1046,6 +1135,19 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return &FInfo->Graph; } + /// Return \p value's PointerKind. + PointerKind getPointerKind(ValueBase *value) const { + auto *f = value->getFunction(); + // The function can be null, e.g. if V is an undef. + if (!f) + return NoPointer; + + return findCachedPointerKind(value->getType(), *f); + } + /// Returns true if \p V may encapsulate a "pointer" value. + /// See EscapeAnalysis::NodeType::Value. + bool isPointer(ValueBase *V) const { return getPointerKind(V) > NoPointer; } + /// Returns true if the value \p V can escape to the function call \p FAS. /// This means that the called function may access the value \p V. /// If \p V has reference semantics, this function returns false if only the @@ -1058,24 +1160,16 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Note that if \p RI is a retain-instruction always false is returned. bool canEscapeTo(SILValue V, RefCountingInst *RI); - /// Returns true if the value \p V can escape to any other pointer \p To. - /// This means that either \p To is the same as \p V or contains a reference - /// to \p V. - bool canEscapeToValue(SILValue V, SILValue To); - - /// Returns true if the parameter with index \p ParamIdx can escape in the - /// called function of apply site \p FAS. - /// If it is an indirect parameter and \p checkContentOfIndirectParam is true - /// then the escape status is not checked for the address itself but for the - /// referenced pointer (if the referenced type is a pointer). - bool canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam); + /// Return true if \p releasedReference deinitialization may release memory + /// pointed to by \p accessedAddress. + bool mayReleaseContent(SILValue releasedReference, SILValue accessedAddress); /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. /// - /// If at least one of the pointers refers to a local object and the - /// connection-graph-nodes of both pointers do not point to the same content + /// First checks that the pointers are known not to alias outside this + /// function, then checks the connection graph to determine that their content + /// is not in the same points-to chain based on access inside this function. bool canPointToSameMemory(SILValue V1, SILValue V2); /// Invalidate all information in this analysis. diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 9096a70b6f09a..5b8005e807db6 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -305,8 +305,6 @@ PASS(OwnershipDumper, "ownership-dumper", "Print Ownership information for Testing") PASS(SemanticARCOpts, "semantic-arc-opts", "Semantic ARC Optimization") -PASS(MarkUninitializedFixup, "mark-uninitialized-fixup", - "Temporary pass for staging in mark_uninitialized changes.") PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks", "Utility pass. Removes all non-term insts from blocks with unreachable terms") PASS(SerializeSILPass, "serialize-sil", diff --git a/include/swift/SILOptimizer/Utils/ConstExpr.h b/include/swift/SILOptimizer/Utils/ConstExpr.h index 85f01ebbd0964..aa2cf66a086c7 100644 --- a/include/swift/SILOptimizer/Utils/ConstExpr.h +++ b/include/swift/SILOptimizer/Utils/ConstExpr.h @@ -207,6 +207,8 @@ class ConstExprStepEvaluator { void dumpState(); }; +bool hasConstantEvaluableAnnotation(SILFunction *fun); + bool isConstantEvaluable(SILFunction *fun); /// Return true if and only if the given function \p fun is specially modeled diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index b1ca5dfccb8f0..2abe532c36b5e 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -56,8 +56,101 @@ NullablePtr createIncrementBefore(SILValue ptr, NullablePtr createDecrementBefore(SILValue ptr, SILInstruction *insertpt); +/// A utility for deleting one or more instructions belonging to a function, and +/// cleaning up any dead code resulting from deleting those instructions. Use +/// this utility instead of +/// \c recursivelyDeleteTriviallyDeadInstruction. +class InstructionDeleter { +private: + // A set vector of instructions that are found to be dead. The ordering + // of instructions in this set is important as when a dead instruction is + // removed, new instructions will be generated to fix the lifetime of the + // instruction's operands. This has to be deterministic. + SmallSetVector deadInstructions; + + void deleteInstruction(SILInstruction *inst, + llvm::function_ref callback, + bool fixOperandLifetimes); + +public: + InstructionDeleter() {} + + /// If the instruction \p inst is dead, record it so that it can be cleaned + /// up. + void trackIfDead(SILInstruction *inst); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. This function will add necessary + /// ownership instructions to fix the lifetimes of the operands of \c inst to + /// compensate for its deletion. This function will not clean up dead code + /// resulting from the instruction's removal. To do so, invoke the method \c + /// cleanupDeadCode of this instance, once the SIL of the contaning function + /// is made consistent. + /// + /// \pre the function containing \c inst must be using ownership SIL. + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDeleteAndFixLifetimes( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. If in ownership SIL, use the + /// \c forceDeleteAndFixLifetimes function instead, unless under special + /// circumstances where the client must handle fixing lifetimes of the + /// operands of the deleted instructions. This function will not fix the + /// lifetimes of the operands of \c inst once it is deleted. This function + /// will not clean up dead code resulting from the instruction's removal. To + /// do so, invoke the method \c cleanupDeadCode of this instance, once the SIL + /// of the contaning function is made consistent. + /// + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDelete( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Clean up dead instructions that are tracked by this instance and all + /// instructions that transitively become dead. + /// + /// \pre the function contaning dead instructions must be consistent (i.e., no + /// under or over releases). Note that if \c forceDelete call leaves the + /// function body in an inconsistent state, it needs to be made consistent + /// before this method is invoked. + /// + /// \param callback a callback called whenever an instruction is deleted. + void + cleanUpDeadInstructions(llvm::function_ref callback = + [](SILInstruction *) {}); +}; + +/// If \c inst is dead, delete it and recursively eliminate all code that +/// becomes dead because of that. If more than one instruction must +/// be checked/deleted use the \c InstructionDeleter utility. +/// +/// This function will add necessary compensation code to fix the lifetimes of +/// the operands of the deleted instructions. +/// +/// \pre the SIL function containing the instruction is assumed to be +/// consistent, i.e., does not have under or over releases. +/// +/// \param callback a callback called whenever an instruction is deleted. +void eliminateDeadInstruction( + SILInstruction *inst, llvm::function_ref callback = + [](SILInstruction *) {}); + /// For each of the given instructions, if they are dead delete them -/// along with their dead operands. +/// along with their dead operands. Note this utility must be phased out and +/// replaced by \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The ArrayRef of instructions to be deleted. /// \param force If Force is set, don't check if the top level instructions @@ -69,7 +162,9 @@ void recursivelyDeleteTriviallyDeadInstructions( }); /// If the given instruction is dead, delete it along with its dead -/// operands. +/// operands. Note this utility must be phased out and replaced by +/// \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The instruction to be deleted. /// \param force If Force is set, don't check if the top level instruction is diff --git a/include/swift/Sema/SourceLoader.h b/include/swift/Sema/SourceLoader.h index 004e7eb724ebd..cb8f4d19e9bf9 100644 --- a/include/swift/Sema/SourceLoader.h +++ b/include/swift/Sema/SourceLoader.h @@ -56,7 +56,7 @@ class SourceLoader : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -69,7 +69,7 @@ class SourceLoader : public ModuleLoader { /// returns NULL. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Load extensions to the given nominal type. /// diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 094941bcb50ac..7247ee8b34bc9 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -50,7 +50,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { void collectVisibleTopLevelModuleNamesImpl(SmallVectorImpl &names, StringRef extension) const; - using AccessPathElem = std::pair; + using AccessPathElem = Located; bool findModule(AccessPathElem moduleID, SmallVectorImpl *moduleInterfacePath, std::unique_ptr *moduleBuffer, @@ -140,7 +140,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -153,7 +153,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// emits a diagnostic and returns a FailedImportModule object. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; virtual void loadExtensions(NominalTypeDecl *nominal, @@ -240,10 +240,10 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase { public: virtual ~MemoryBufferSerializedModuleLoader(); - bool canImportModule(std::pair named) override; + bool canImportModule(Located named) override; ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Register a memory buffer that contains the serialized module for the given /// access path. This API is intended to be used by LLDB to add swiftmodules diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 43108d5736019..57b4fd6e31803 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -25,6 +25,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Mutex.h" #include @@ -118,18 +119,14 @@ namespace swift { /// /// \param PersistentState if non-null the same PersistentState object can /// be used to resume parsing or parse delayed function bodies. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, + void parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL = nullptr, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); /// Parse a single buffer into the given source file, until the full source /// contents are parsed. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, + void parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); @@ -277,7 +274,8 @@ namespace swift { StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Turn the given Swift module into either LLVM IR or native code /// and return the generated LLVM IR module. @@ -287,7 +285,8 @@ namespace swift { std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Given an already created LLVM module, construct a pass pipeline and run /// the Swift LLVM Pipeline upon it. This does not cause the module to be diff --git a/include/swift/SwiftRemoteMirror/Platform.h b/include/swift/SwiftRemoteMirror/Platform.h index cefdea2c4de6f..1ec61dde9cad9 100644 --- a/include/swift/SwiftRemoteMirror/Platform.h +++ b/include/swift/SwiftRemoteMirror/Platform.h @@ -18,7 +18,7 @@ extern "C" { #endif #if defined(swiftRemoteMirror_EXPORTS) -# if defined(__ELF__) +# if defined(__ELF__) || defined(__wasm__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("protected"))) # elif defined(__MACH__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) @@ -30,7 +30,7 @@ extern "C" { # endif # endif #else -# if defined(__ELF__) +# if defined(__ELF__) || defined(__wasm__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) # elif defined(__MACH__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) diff --git a/include/swift/SymbolGraphGen/SymbolGraphGen.h b/include/swift/SymbolGraphGen/SymbolGraphGen.h new file mode 100644 index 0000000000000..324a505c45e39 --- /dev/null +++ b/include/swift/SymbolGraphGen/SymbolGraphGen.h @@ -0,0 +1,41 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "swift/AST/AttrKind.h" + +namespace swift { + +class ModuleDecl; + +namespace symbolgraphgen { + +struct SymbolGraphOptions { + /// The path to output the symbol graph JSON. + StringRef OutputPath; + + /// The target of the module. + llvm::Triple Target; + + /// Pretty-print the JSON with newlines and indentation. + bool PrettyPrint; + + /// The minimum access level that symbols must have in order to be + /// included in the graph. + AccessLevel MinimumAccessLevel; +}; + +/// Emit a Symbol Graph JSON file for a module. +int emitSymbolGraphForModule(ModuleDecl *M, const SymbolGraphOptions &Options); + +} // end namespace symbolgraphgen +} // end namespace swift diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index c78803ff58e38..d4cfad2175080 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -33,6 +33,9 @@ struct TBDGenOptions { /// Whether this compilation is producing a TBD for InstallAPI. bool IsInstallAPI; + /// Only collect linker directive symbols. + bool LinkerDirectivesOnly = false; + /// The install_name to use in the TBD file. std::string InstallName; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 805f472be89d4..5228ba6c7ca9b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -196,6 +196,9 @@ struct ASTContext::Implementation { /// The declaration of '+' function for two String. FuncDecl *PlusFunctionOnString = nullptr; + /// The declaration of 'Sequence.makeIterator()'. + FuncDecl *MakeIterator = nullptr; + /// The declaration of Swift.Optional.Some. EnumElementDecl *OptionalSomeDecl = nullptr; @@ -530,7 +533,9 @@ ASTContext::ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, : LangOpts(langOpts), TypeCheckerOpts(typeckOpts), SearchPathOpts(SearchPathOpts), SourceMgr(SourceMgr), Diags(Diags), - evaluator(Diags, langOpts.DebugDumpCycles), + evaluator(Diags, + langOpts.DebugDumpCycles, + langOpts.BuildRequestDependencyGraph), TheBuiltinModule(createBuiltinModule(*this)), StdlibModuleName(getIdentifier(STDLIB_NAME)), SwiftShimsModuleName(getIdentifier(SWIFT_SHIMS_NAME)), @@ -710,6 +715,31 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const { return getImpl().PlusFunctionOnString; } +FuncDecl *ASTContext::getSequenceMakeIterator() const { + if (getImpl().MakeIterator) { + return getImpl().MakeIterator; + } + + auto proto = getProtocol(KnownProtocolKind::Sequence); + if (!proto) + return nullptr; + + for (auto result : proto->lookupDirect(Id_makeIterator)) { + if (result->getDeclContext() != proto) + continue; + + if (auto func = dyn_cast(result)) { + if (func->getParameters()->size() != 0) + continue; + + getImpl().MakeIterator = func; + return func; + } + } + + return nullptr; +} + #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ if (getImpl().NAME##Decl) \ @@ -1468,12 +1498,12 @@ ClangModuleLoader *ASTContext::getDWARFModuleLoader() const { } ModuleDecl *ASTContext::getLoadedModule( - ArrayRef> ModulePath) const { + ArrayRef> ModulePath) const { assert(!ModulePath.empty()); // TODO: Swift submodules. if (ModulePath.size() == 1) { - return getLoadedModule(ModulePath[0].first); + return getLoadedModule(ModulePath[0].Item); } return nullptr; } @@ -1723,13 +1753,13 @@ bool ASTContext::shouldPerformTypoCorrection() { return NumTypoCorrections <= LangOpts.TypoCorrectionLimit; } -bool ASTContext::canImportModule(std::pair ModulePath) { +bool ASTContext::canImportModule(Located ModulePath) { // If this module has already been successfully imported, it is importable. if (getLoadedModule(ModulePath) != nullptr) return true; // If we've failed loading this module before, don't look for it again. - if (FailedModuleImportNames.count(ModulePath.first)) + if (FailedModuleImportNames.count(ModulePath.Item)) return false; // Otherwise, ask the module loaders. @@ -1739,12 +1769,12 @@ bool ASTContext::canImportModule(std::pair ModulePath) { } } - FailedModuleImportNames.insert(ModulePath.first); + FailedModuleImportNames.insert(ModulePath.Item); return false; } ModuleDecl * -ASTContext::getModule(ArrayRef> ModulePath) { +ASTContext::getModule(ArrayRef> ModulePath) { assert(!ModulePath.empty()); if (auto *M = getLoadedModule(ModulePath)) @@ -1752,7 +1782,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { auto moduleID = ModulePath[0]; for (auto &importer : getImpl().ModuleLoaders) { - if (ModuleDecl *M = importer->loadModule(moduleID.second, ModulePath)) { + if (ModuleDecl *M = importer->loadModule(moduleID.Loc, ModulePath)) { return M; } } @@ -1761,7 +1791,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { } ModuleDecl *ASTContext::getModuleByName(StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1778,7 +1808,7 @@ ModuleDecl *ASTContext::getStdlibModule(bool loadIfAbsent) { if (loadIfAbsent) { auto mutableThis = const_cast(this); TheStdlibModule = - mutableThis->getModule({ std::make_pair(StdlibModuleName, SourceLoc()) }); + mutableThis->getModule({ Located(StdlibModuleName, SourceLoc()) }); } else { TheStdlibModule = getLoadedModule(StdlibModuleName); } @@ -2893,9 +2923,10 @@ void AnyFunctionType::decomposeInput( } default: - result.emplace_back(type->getInOutObjectType(), Identifier(), - ParameterTypeFlags::fromParameterType( - type, false, false, false, ValueOwnership::Default)); + result.emplace_back( + type->getInOutObjectType(), Identifier(), + ParameterTypeFlags::fromParameterType(type, false, false, false, + ValueOwnership::Default, false)); return; } } @@ -3127,6 +3158,11 @@ ArrayRef GenericFunctionType::getRequirements() const { return Signature->getRequirements(); } +void SILFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) const { + cml->printClangType(ClangFunctionType, os); +} + void SILFunctionType::Profile( llvm::FoldingSetNodeID &id, GenericSignature genericParams, @@ -3939,18 +3975,13 @@ void DeclName::CompoundDeclName::Profile(llvm::FoldingSetNodeID &id, void DeclName::initialize(ASTContext &C, DeclBaseName baseName, ArrayRef argumentNames) { - if (argumentNames.empty()) { - SimpleOrCompound = BaseNameAndCompound(baseName, true); - return; - } - llvm::FoldingSetNodeID id; CompoundDeclName::Profile(id, baseName, argumentNames); void *insert = nullptr; if (CompoundDeclName *compoundName = C.getImpl().CompoundNames.FindNodeOrInsertPos(id, insert)) { - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; return; } @@ -3960,7 +3991,7 @@ void DeclName::initialize(ASTContext &C, DeclBaseName baseName, auto compoundName = new (buf) CompoundDeclName(baseName,argumentNames.size()); std::uninitialized_copy(argumentNames.begin(), argumentNames.end(), compoundName->getArgumentNames().begin()); - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; C.getImpl().CompoundNames.InsertNode(compoundName, insert); } @@ -4401,11 +4432,13 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, const ValueDecl *derived) { auto baseGenericCtx = base->getAsGenericContext(); auto derivedGenericCtx = derived->getAsGenericContext(); - auto &ctx = base->getASTContext(); if (!baseGenericCtx || !derivedGenericCtx) return nullptr; + if (base == derived) + return derivedGenericCtx->getGenericSignature(); + auto baseClass = base->getDeclContext()->getSelfClassDecl(); if (!baseClass) return nullptr; @@ -4461,7 +4494,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } return CanGenericTypeParamType::get( - gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), ctx); + gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), *this); }; auto lookupConformanceFn = @@ -4481,7 +4514,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } auto genericSig = evaluateOrDefault( - ctx.evaluator, + evaluator, AbstractGenericSignatureRequest{ derivedClass->getGenericSignature().getPointer(), std::move(addedGenericParams), diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 08e03ecfed560..413f54db7cc79 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -589,7 +589,7 @@ namespace { OS << " '"; interleave(ID->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { - OS << Elem.first; + OS << Elem.Item; }, [&] { OS << '.'; }); OS << "')"; @@ -794,7 +794,7 @@ namespace { PrintWithColorRAII(OS, ASTNodeColor) << "source_file "; PrintWithColorRAII(OS, LocationColor) << '\"' << SF.getFilename() << '\"'; - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { if (D->isImplicit()) continue; @@ -1593,12 +1593,6 @@ class PrintStmt : public StmtVisitor { } void visitForEachStmt(ForEachStmt *S) { printCommon(S, "for_each_stmt"); - PrintWithColorRAII(OS, LiteralValueColor) << " make_generator="; - S->getMakeIterator().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); - PrintWithColorRAII(OS, LiteralValueColor) << " next="; - S->getIteratorNext().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); OS << '\n'; printRec(S->getPattern()); OS << '\n'; diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 9e2a0f9416144..493832ce0fc72 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -499,6 +499,7 @@ std::string ASTMangler::mangleTypeAsUSR(Type Ty) { std::string ASTMangler::mangleDeclAsUSR(const ValueDecl *Decl, StringRef USRPrefix) { + DWARFMangling = true; beginManglingWithoutPrefix(); llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); Buffer << USRPrefix; @@ -1790,7 +1791,12 @@ void ASTMangler::appendModule(const ModuleDecl *module, assert(useModuleName.empty()); return appendOperator("SC"); } - if (!useModuleName.empty()) + + // Enabling DWARFMangling indicate the mangled names are not part of the ABI, + // probably used by the debugger or IDE (USR). These mangled names will not be + // demangled successfully if we use the original module name instead of the + // actual module name. + if (!useModuleName.empty() && !DWARFMangling) appendIdentifier(useModuleName); else appendIdentifier(ModName); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8ae9c430aae4b..1fe355d76bc47 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -56,6 +57,8 @@ using namespace swift; void PrintOptions::setBaseType(Type T) { + if (T->is()) + return; TransformContext = TypeTransformContext(T); } @@ -96,7 +99,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { +PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention) { PrintOptions result; result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; @@ -113,6 +117,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { result.OpaqueReturnTypePrinting = OpaqueReturnTypePrintingMode::StableReference; result.PreferTypeRepr = preferTypeRepr; + if (printFullConvention) + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::Full; // We should print __consuming, __owned, etc for the module interface file. result.SkipUnderscoredKeywords = false; @@ -319,6 +326,14 @@ void ASTPrinter::callPrintDeclPre(const Decl *D, printDeclPre(D, Bracket); } +ASTPrinter &ASTPrinter::operator<<(QuotedString s) { + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + OS << s; + printTextImpl(OS.str()); + return *this; +} + ASTPrinter &ASTPrinter::operator<<(unsigned long long N) { llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -670,6 +685,7 @@ class PrintAST : public ASTVisitor { FreshOptions.ExcludeAttrList = options.ExcludeAttrList; FreshOptions.ExclusiveAttrList = options.ExclusiveAttrList; FreshOptions.PrintOptionalAsImplicitlyUnwrapped = options.PrintOptionalAsImplicitlyUnwrapped; + FreshOptions.TransformContext = options.TransformContext; T.print(Printer, FreshOptions); return; } @@ -700,6 +716,8 @@ class PrintAST : public ASTVisitor { } T = T.subst(subMap, SubstFlags::DesugarMemberTypes); + + options.TransformContext = TypeTransformContext(CurrentType); } printTypeWithOptions(T, options); @@ -985,6 +1003,22 @@ void PrintAST::printAttributes(const Decl *D) { Printer << " "; } } + + // If the declaration has designated inits that won't be visible to + // clients, or if it inherits superclass convenience initializers, + // then print those attributes specially. + if (auto CD = dyn_cast(D)) { + if (Options.PrintImplicitAttrs) { + if (CD->inheritsSuperclassInitializers()) { + Printer.printAttrName("@_inheritsConvenienceInitializers"); + Printer << " "; + } + if (CD->hasMissingDesignatedInitializers()) { + Printer.printAttrName("@_hasMissingDesignatedInitializers"); + Printer << " "; + } + } + } } D->getAttrs().print(Printer, Options, D); @@ -2092,10 +2126,10 @@ void PrintAST::visitImportDecl(ImportDecl *decl) { interleave(decl->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { if (!Mods.empty()) { - Printer.printModuleRef(Mods.front(), Elem.first); + Printer.printModuleRef(Mods.front(), Elem.Item); Mods = Mods.slice(1); } else { - Printer << Elem.first.str(); + Printer << Elem.Item.str(); } }, [&] { Printer << "."; }); @@ -2497,6 +2531,8 @@ static void printParameterFlags(ASTPrinter &printer, PrintOptions options, ParameterTypeFlags flags, bool escaping) { if (!options.excludeAttrKind(TAK_autoclosure) && flags.isAutoClosure()) printer << "@autoclosure "; + if (!options.excludeAttrKind(TAK_noDerivative) && flags.isNoDerivative()) + printer << "@noDerivative "; switch (flags.getValueOwnership()) { case ValueOwnership::Default: @@ -2596,6 +2632,9 @@ void PrintAST::printOneParameter(const ParamDecl *param, // Else, print the argument only. LLVM_FALLTHROUGH; case PrintOptions::ArgAndParamPrintingMode::ArgumentOnly: + if (ArgName.empty() && !Options.PrintEmptyArgumentNames) { + return; + } Printer.printName(ArgName, PrintNameContext::FunctionParameterExternal); if (!ArgNameIsAPIByDefault && !ArgName.empty()) @@ -2650,7 +2689,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, if (param->isVariadic()) Printer << "..."; - if (param->isDefaultArgument()) { + if (param->isDefaultArgument() && Options.PrintDefaultArgumentValue) { SmallString<128> scratch; auto defaultArgStr = param->getDefaultValueStringRepresentation(scratch); @@ -3003,10 +3042,10 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { Printer << " -> "; TypeLoc elementTy = decl->getElementTypeLoc(); - Printer.printDeclResultTypePre(decl, elementTy); - Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); if (!elementTy.getTypeRepr()) elementTy = TypeLoc::withoutLoc(decl->getElementInterfaceType()); + Printer.printDeclResultTypePre(decl, elementTy); + Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); // HACK: When printing result types for subscripts with opaque result types, // always print them using the `some` keyword instead of printing @@ -3455,6 +3494,15 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { // Type Printing //===----------------------------------------------------------------------===// +template +void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { + auto *cml = Ctx.getClangModuleLoader(); + SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + info.getUncommonInfo().getValue().printClangFunctionType(cml, os); + Printer << ", cType: " << QuotedString(os.str()); +} + namespace { class TypePrinter : public TypeVisitor { using super = TypeVisitor; @@ -3591,7 +3639,6 @@ class TypePrinter : public TypeVisitor { // know we're printing a type member so it escapes `Type` and `Protocol`. if (auto parent = Ty->getParent()) { visitParentType(parent); - Printer << "."; NameContext = PrintNameContext::TypeMember; } else if (shouldPrintFullyQualified(Ty)) { printModuleContext(Ty); @@ -3731,13 +3778,32 @@ class TypePrinter : public TypeVisitor { } void visitParentType(Type T) { + /// Don't print the parent type if it's being printed in that type context. + if (Options.TransformContext) { + if (auto currentType = Options.TransformContext->getBaseType()) { + auto printingType = T; + if (currentType->hasArchetype()) + currentType = currentType->mapTypeOutOfContext(); + + if (auto errorTy = printingType->getAs()) + if (auto origTy = errorTy->getOriginalType()) + printingType = origTy; + + if (printingType->hasArchetype()) + printingType = printingType->mapTypeOutOfContext(); + + if (currentType->isEqual(printingType)) + return; + } + } PrintOptions innerOptions = Options; innerOptions.SynthesizeSugarOnTypes = false; if (auto sugarType = dyn_cast(T.getPointer())) T = sugarType->getImplementationType(); - TypePrinter(Printer, innerOptions).visit(T); + TypePrinter(Printer, innerOptions).printWithParensIfNotSimple(T); + Printer << "."; } void visitEnumType(EnumType *T) { @@ -3799,7 +3865,7 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { if (Options.SkipAttributes) return; @@ -3812,9 +3878,18 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getSILRepresentation() != SILFunctionType::Representation::Thick) { + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + return; + case PrintOptions::FunctionRepresentationMode::Full: + case PrintOptions::FunctionRepresentationMode::NameOnly: + if (Options.excludeAttrKind(TAK_convention) || + info.getSILRepresentation() == SILFunctionType::Representation::Thick) + return; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3830,6 +3905,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3850,7 +3930,8 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(SILFunctionType::ExtInfo info, + void printFunctionExtInfo(ASTContext &Ctx, + SILFunctionType::ExtInfo info, ProtocolConformanceRef witnessMethodConformance) { if (Options.SkipAttributes) return; @@ -3864,9 +3945,19 @@ class TypePrinter : public TypeVisitor { } } - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getRepresentation() != SILFunctionType::Representation::Thick) { + + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + break; + case PrintOptions::FunctionRepresentationMode::NameOnly: + case PrintOptions::FunctionRepresentationMode::Full: + if (Options.excludeAttrKind(TAK_convention) || + info.getRepresentation() == SILFunctionType::Representation::Thick) + break; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3881,6 +3972,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3950,7 +4046,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -3987,7 +4083,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -4040,7 +4136,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getExtInfo(), + printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); @@ -4247,8 +4343,7 @@ class TypePrinter : public TypeVisitor { } void visitNestedArchetypeType(NestedArchetypeType *T) { - printWithParensIfNotSimple(T->getParent()); - Printer << "."; + visitParentType(T->getParent()); printArchetypeCommon(T); } @@ -4339,7 +4434,6 @@ class TypePrinter : public TypeVisitor { void visitDependentMemberType(DependentMemberType *T) { visitParentType(T->getBase()); - Printer << "."; Printer.printName(T->getName()); } diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 4c1966841c53e..bb54182e1f0fd 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -1193,7 +1193,7 @@ AnnotatedInsertionPoint ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); - ArrayRef decls = SF->Decls; + ArrayRef decls = SF->getTopLevelDecls(); // Assume that decls are only added at the end, in source order ArrayRef newDecls = decls.slice(numberOfDeclsAlreadySeen); std::vector newNodes(newDecls.begin(), newDecls.end()); @@ -1865,10 +1865,10 @@ void ASTScopeImpl::beCurrent() {} bool ASTScopeImpl::isCurrentIfWasExpanded() const { return true; } void ASTSourceFileScope::beCurrent() { - numberOfDeclsAlreadySeen = SF->Decls.size(); + numberOfDeclsAlreadySeen = SF->getTopLevelDecls().size(); } bool ASTSourceFileScope::isCurrentIfWasExpanded() const { - return SF->Decls.size() == numberOfDeclsAlreadySeen; + return SF->getTopLevelDecls().size() == numberOfDeclsAlreadySeen; } void IterableTypeScope::beCurrent() { portion->beCurrent(this); } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 07d6d7d40921b..be34aafee6885 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -517,12 +517,59 @@ GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC( while (i != 0) { Optional> maybeSelfDC = history[--i]->computeSelfDCForParent(); - if (maybeSelfDC) - return *maybeSelfDC; + if (maybeSelfDC) { + // If we've found a selfDC, we'll definitely be returning something. + // However, we may have captured 'self' somewhere down the tree, so we + // can't return outright without checking the nested scopes. + NullablePtr nestedCapturedSelfDC = + checkNestedScopesForSelfCapture(history, i); + return nestedCapturedSelfDC ? nestedCapturedSelfDC : *maybeSelfDC; + } } return nullptr; } +#pragma mark checkNestedScopesForSelfCapture + +NullablePtr +GenericTypeOrExtensionWhereOrBodyPortion::checkNestedScopesForSelfCapture( + ArrayRef history, size_t start) { + NullablePtr innerCapturedSelfDC; + // Start with the next scope down the tree. + size_t j = start; + + // Note: even though having this loop nested inside the while loop from + // GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC may appear to + // result in quadratic blowup, complexity actually remains linear with respect + // to the size of history. This relies on the fact that + // GenericTypeOrExtensionScope::computeSelfDCForParent returns a null pointer, + // which will cause this method to bail out of the search early. Thus, this + // method is called once per type body in the lookup history, and will not + // end up re-checking the bodies of nested types that have already been + // covered by earlier calls, so the total impact of this method across all + // calls in a single lookup is O(n). + while (j != 0) { + auto *entry = history[--j]; + Optional> selfDCForParent = + entry->computeSelfDCForParent(); + + // If we encounter a scope that should cause us to forget the self + // context (such as a nested type), bail out and use whatever the + // the last inner captured context was. + if (selfDCForParent && (*selfDCForParent).isNull()) + break; + + // Otherwise, if we have a captured self context for this scope, then + // remember it since it is now the innermost scope we have encountered. + NullablePtr capturedSelfDC = entry->capturedSelfDC(); + if (!capturedSelfDC.isNull()) + innerCapturedSelfDC = entry->capturedSelfDC(); + + // Continue searching in the next scope down. + } + return innerCapturedSelfDC; +} + #pragma mark compute isCascadingUse Optional ASTScopeImpl::computeIsCascadingUse( @@ -631,6 +678,23 @@ MethodBodyScope::computeSelfDCForParent() const { return NullablePtr(decl); } +#pragma mark capturedSelfDC + +// Closures may explicitly capture the self param, in which case the lookup +// should use the closure as the context for implicit self lookups. + +// By default, there is no such context to return. +NullablePtr ASTScopeImpl::capturedSelfDC() const { + return NullablePtr(); +} + +// Closures track this information explicitly. +NullablePtr ClosureParametersScope::capturedSelfDC() const { + if (closureExpr->capturesSelfEnablingImplictSelf()) + return NullablePtr(closureExpr); + return NullablePtr(); +} + #pragma mark ifUnknownIsCascadingUseAccordingTo static bool isCascadingUseAccordingTo(const DeclContext *const dc) { diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index c5689881e176f..99a38518883c5 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -284,12 +284,12 @@ SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode( return SourceRange(charRange.getStart(), charRange.getEnd()); } - if (SF->Decls.empty()) + if (SF->getTopLevelDecls().empty()) return SourceRange(); // Use the source ranges of the declarations in the file. - return SourceRange(SF->Decls.front()->getStartLoc(), - SF->Decls.back()->getEndLoc()); + return SourceRange(SF->getTopLevelDecls().front()->getStartLoc(), + SF->getTopLevelDecls().back()->getEndLoc()); } SourceRange GenericTypeOrExtensionScope::getSourceRangeOfThisASTNode( diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 728a1cb97673d..61e91cf14dc07 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -92,8 +92,8 @@ void TypeAttributes::getConventionArguments(SmallVectorImpl &buf) const { stream << ": " << convention.WitnessMethodProtocol; return; } - if (!convention.ClangType.empty()) - stream << ", cType: " << QuotedString(convention.ClangType); + if (!convention.ClangType.Item.empty()) + stream << ", cType: " << QuotedString(convention.ClangType.Item); } /// Given a name like "autoclosure", return the type attribute ID that @@ -366,25 +366,46 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } -// Returns the differentiation parameters clause string for the given function, -// parameter indices, and parsed parameters. +/// The kind of a parameter in a `wrt:` differentiation parameters clause: +/// either a differentiability parameter or a linearity parameter. Used for +/// printing `@differentiable`, `@derivative`, and `@transpose` attributes. +enum class DifferentiationParameterKind { + /// A differentiability parameter, printed by name. + /// Used for `@differentiable` and `@derivative` attribute. + Differentiability, + /// A linearity parameter, printed by index. + /// Used for `@transpose` attribute. + Linearity +}; + +/// Returns the differentiation parameters clause string for the given function, +/// parameter indices, parsed parameters, and differentiation parameter kind. +/// Use the parameter indices if specified; otherwise, use the parsed +/// parameters. static std::string getDifferentiationParametersClauseString( - const AbstractFunctionDecl *function, IndexSubset *paramIndices, - ArrayRef parsedParams) { + const AbstractFunctionDecl *function, IndexSubset *parameterIndices, + ArrayRef parsedParams, + DifferentiationParameterKind parameterKind) { assert(function); bool isInstanceMethod = function->isInstanceMember(); + bool isStaticMethod = function->isStatic(); std::string result; llvm::raw_string_ostream printer(result); // Use the parameter indices, if specified. - if (paramIndices) { - auto parameters = paramIndices->getBitVector(); + if (parameterIndices) { + auto parameters = parameterIndices->getBitVector(); auto parameterCount = parameters.count(); printer << "wrt: "; if (parameterCount > 1) printer << '('; // Check if differentiating wrt `self`. If so, manually print it first. - if (isInstanceMethod && parameters.test(parameters.size() - 1)) { + bool isWrtSelf = + (isInstanceMethod || + (isStaticMethod && + parameterKind == DifferentiationParameterKind::Linearity)) && + parameters.test(parameters.size() - 1); + if (isWrtSelf) { parameters.reset(parameters.size() - 1); printer << "self"; if (parameters.any()) @@ -392,7 +413,16 @@ static std::string getDifferentiationParametersClauseString( } // Print remaining differentiation parameters. interleave(parameters.set_bits(), [&](unsigned index) { - printer << function->getParameters()->get(index)->getName().str(); + switch (parameterKind) { + // Print differentiability parameters by name. + case DifferentiationParameterKind::Differentiability: + printer << function->getParameters()->get(index)->getName().str(); + break; + // Print linearity parameters by index. + case DifferentiationParameterKind::Linearity: + printer << index; + break; + } }, [&] { printer << ", "; }); if (parameterCount > 1) printer << ')'; @@ -425,11 +455,11 @@ static std::string getDifferentiationParametersClauseString( return printer.str(); } -// Print the arguments of the given `@differentiable` attribute. -// - If `omitWrtClause` is true, omit printing the `wrt:` differentiation -// parameters clause. -// - If `omitDerivativeFunctions` is true, omit printing the JVP/VJP derivative -// functions. +/// Print the arguments of the given `@differentiable` attribute. +/// - If `omitWrtClause` is true, omit printing the `wrt:` differentiation +/// parameters clause. +/// - If `omitDerivativeFunctions` is true, omit printing the JVP/VJP derivative +/// functions. static void printDifferentiableAttrArguments( const DifferentiableAttr *attr, ASTPrinter &printer, PrintOptions Options, const Decl *D, bool omitWrtClause = false, @@ -465,7 +495,8 @@ static void printDifferentiableAttrArguments( // Print differentiation parameters clause, unless it is to be omitted. if (!omitWrtClause) { auto diffParamsString = getDifferentiationParametersClauseString( - original, attr->getParameterIndices(), attr->getParsedParameters()); + original, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Differentiability); // Check whether differentiation parameter clause is empty. // Handles edge case where resolved parameter indices are unset and // parsed parameters are empty. This case should never trigger for @@ -904,13 +935,29 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << attr->getOriginalFunctionName().Name; auto *derivative = cast(D); auto diffParamsString = getDifferentiationParametersClauseString( - derivative, attr->getParameterIndices(), attr->getParsedParameters()); + derivative, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Differentiability); if (!diffParamsString.empty()) Printer << ", " << diffParamsString; Printer << ')'; break; } + case DAK_Transpose: { + Printer.printAttrName("@transpose"); + Printer << "(of: "; + auto *attr = cast(this); + Printer << attr->getOriginalFunctionName().Name; + auto *transpose = cast(D); + auto transParamsString = getDifferentiationParametersClauseString( + transpose, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Linearity); + if (!transParamsString.empty()) + Printer << ", " << transParamsString; + Printer << ')'; + break; + } + case DAK_ImplicitlySynthesizesNestedRequirement: Printer.printAttrName("@_implicitly_synthesizes_nested_requirement"); Printer << "(\"" << cast(this)->Value << "\")"; @@ -1054,6 +1101,8 @@ StringRef DeclAttribute::getAttrName() const { return "differentiable"; case DAK_Derivative: return "derivative"; + case DAK_Transpose: + return "transpose"; } llvm_unreachable("bad DeclAttrKind"); } @@ -1244,6 +1293,10 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const { return isPlatformActive(Platform, ctx.LangOpts); } +bool OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const { + return isPlatformActive(Platform, ctx.LangOpts); +} + bool AvailableAttr::isLanguageVersionSpecific() const { if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::SwiftVersionSpecific) @@ -1409,7 +1462,8 @@ DifferentiableAttr::DifferentiableAttr(Decl *original, bool implicit, Optional vjp, GenericSignature derivativeGenSig) : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), - Linear(linear), JVP(std::move(jvp)), VJP(std::move(vjp)) { + OriginalDeclaration(original), Linear(linear), JVP(std::move(jvp)), + VJP(std::move(vjp)) { setParameterIndices(parameterIndices); setDerivativeGenericSignature(derivativeGenSig); } @@ -1444,6 +1498,13 @@ DifferentiableAttr::create(AbstractFunctionDecl *original, bool implicit, std::move(vjp), derivativeGenSig); } +void DifferentiableAttr::setOriginalDeclaration(Decl *originalDeclaration) { + assert(originalDeclaration && "Original declaration must be non-null"); + assert(!OriginalDeclaration && + "Original declaration cannot have already been set"); + OriginalDeclaration = originalDeclaration; +} + void DifferentiableAttr::setJVPFunction(FuncDecl *decl) { JVPFunction = decl; if (decl && !JVP) @@ -1466,49 +1527,91 @@ GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause, - bool omitAssociatedFunctions) const { + bool omitDerivativeFunctions) const { StreamPrinter P(OS); P << "@" << getAttrName(); printDifferentiableAttrArguments(this, P, PrintOptions(), D, omitWrtClause, - omitAssociatedFunctions); + omitDerivativeFunctions); } DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, - SourceRange baseRange, + SourceRange baseRange, TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, ArrayRef params) : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), - OriginalFunctionName(std::move(originalName)), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), NumParsedParameters(params.size()) { std::copy(params.begin(), params.end(), getTrailingObjects()); } DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, - SourceRange baseRange, + SourceRange baseRange, TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, - IndexSubset *indices) + IndexSubset *parameterIndices) : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), - OriginalFunctionName(std::move(originalName)), ParameterIndices(indices) { -} + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(parameterIndices) {} DerivativeAttr * DerivativeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, - SourceRange baseRange, DeclNameRefWithLoc originalName, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, ArrayRef params) { unsigned size = totalSizeToAlloc(params.size()); void *mem = context.Allocate(size, alignof(DerivativeAttr)); - return new (mem) DerivativeAttr(implicit, atLoc, baseRange, + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, std::move(originalName), params); } DerivativeAttr *DerivativeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc originalName, - IndexSubset *indices) { + IndexSubset *parameterIndices) { void *mem = context.Allocate(sizeof(DerivativeAttr), alignof(DerivativeAttr)); - return new (mem) DerivativeAttr(implicit, atLoc, baseRange, - std::move(originalName), indices); + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, + std::move(originalName), parameterIndices); +} + +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + ArrayRef params) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + NumParsedParameters(params.size()) { + std::uninitialized_copy(params.begin(), params.end(), + getTrailingObjects()); +} + +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(parameterIndices) {} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + ArrayRef params) { + unsigned size = totalSizeToAlloc(params.size()); + void *mem = context.Allocate(size, alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), params); +} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) { + void *mem = context.Allocate(sizeof(TransposeAttr), alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), parameterIndices); } ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 912e5617867a4..579fe7f0f3c24 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -239,14 +239,25 @@ AvailabilityContext ASTContext::getSwift51Availability() { } AvailabilityContext ASTContext::getTypesInAbstractMetadataStateAvailability() { + return getSwift52Availability(); +} + +AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() { + return getSwift52Availability(); +} + +AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; if (target.isMacOSX() ) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(10, 99, 0))); - } else if (target.isiOS() || target.isWatchOS()) { + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(99, 0, 0))); + } else if (target.isWatchOS()) { return AvailabilityContext( - VersionRange::allGTE(llvm::VersionTuple(9999, 0, 0))); + VersionRange::allGTE(llvm::VersionTuple(9, 99, 0))); } else { return AvailabilityContext::alwaysAvailable(); } diff --git a/lib/AST/CaptureInfo.cpp b/lib/AST/CaptureInfo.cpp index 3ea6088d88f89..382db84fe7170 100644 --- a/lib/AST/CaptureInfo.cpp +++ b/lib/AST/CaptureInfo.cpp @@ -57,7 +57,7 @@ CaptureInfo CaptureInfo::empty() { bool CaptureInfo::hasLocalCaptures() const { for (auto capture : getCaptures()) - if (capture.getDecl()->getDeclContext()->isLocalContext()) + if (capture.getDecl()->isLocalCapture()) return true; return false; } @@ -71,7 +71,7 @@ getLocalCaptures(SmallVectorImpl &Result) const { // Filter out global variables. for (auto capture : getCaptures()) { - if (!capture.getDecl()->getDeclContext()->isLocalContext()) + if (!capture.getDecl()->isLocalCapture()) continue; Result.push_back(capture); diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 642322ce76b07..414d3d8803984 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -140,7 +140,7 @@ void ConformanceLookupTable::destroy() { } namespace { - using ConformanceConstructionInfo = std::pair; + using ConformanceConstructionInfo = Located; } template @@ -194,14 +194,14 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { - protocols.push_back({SourceLoc(), conf->getProtocol()}); + protocols.push_back({conf->getProtocol(), SourceLoc()}); } } else if (next->getParentSourceFile()) { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { - if (auto proto = dyn_cast(found.second)) - protocols.push_back({found.first, proto}); + if (auto proto = dyn_cast(found.Item)) + protocols.push_back({proto, found.Loc}); } } @@ -281,7 +281,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) - addProtocol(locAndProto.second, locAndProto.first, source); + addProtocol(locAndProto.Item, locAndProto.Loc, source); }); break; @@ -470,8 +470,8 @@ void ConformanceLookupTable::addInheritedProtocols( bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.second)) - addProtocol(proto, found.first, source); + if (auto proto = dyn_cast(found.Item)) + addProtocol(proto, found.Loc, source); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 0bbe74f098d04..0d9f405879024 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -577,8 +577,12 @@ Result->X = SM.getLocFromExternalSource(Locs->SourceFilePath, Locs->X.Line, \ } StringRef Decl::getAlternateModuleName() const { - if (auto *OD = Attrs.getAttribute(DeclAttrKind::DAK_OriginallyDefinedIn)) { - return static_cast(OD)->OriginalModuleName; + for (auto *Att: Attrs) { + if (auto *OD = dyn_cast(Att)) { + if (OD->isActivePlatform(getASTContext())) { + return OD->OriginalModuleName; + } + } } for (auto *DC = getDeclContext(); DC; DC = DC->getParent()) { if (auto decl = DC->getAsDecl()) { @@ -693,6 +697,56 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const { return false; } +bool Decl::hasUnderscoredNaming() const { + const Decl *D = this; + if (const auto AFD = dyn_cast(D)) { + // If it's a function with a parameter with leading underscore, it's a + // private function. + if (AFD->getParameters()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto SubscriptD = dyn_cast(D)) { + if (SubscriptD->getIndices()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto PD = dyn_cast(D)) { + if (PD->getAttrs().hasAttribute()) { + return false; + } + StringRef NameStr = PD->getNameStr(); + if (NameStr.startswith("_Builtin")) { + return true; + } + if (NameStr.startswith("_ExpressibleBy")) { + return true; + } + } + + if (const auto ImportD = dyn_cast(D)) { + if (const auto *Mod = ImportD->getModule()) { + if (Mod->isSwiftShimsModule()) { + return true; + } + } + } + + const auto VD = dyn_cast(D); + if (!VD || !VD->hasName()) { + return false; + } + + if (!VD->getBaseName().isSpecial() && + VD->getBaseName().getIdentifier().str().startswith("_")) { + return true; + } + + return false; +} + bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { const Decl *D = this; if (auto ExtD = dyn_cast(D)) { @@ -714,47 +768,12 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { FU->getKind() != FileUnitKind::SerializedAST) return false; - if (auto AFD = dyn_cast(D)) { - // If it's a function with a parameter with leading underscore, it's a - // private function. - if (AFD->getParameters()->hasInternalParameter("_")) - return true; - } - - if (auto SubscriptD = dyn_cast(D)) { - if (SubscriptD->getIndices()->hasInternalParameter("_")) - return true; - } - if (auto PD = dyn_cast(D)) { - if (PD->getAttrs().hasAttribute()) - return false; - StringRef NameStr = PD->getNameStr(); - if (NameStr.startswith("_Builtin")) - return true; - if (NameStr.startswith("_ExpressibleBy")) - return true; if (treatNonBuiltinProtocolsAsPublic) return false; } - if (auto ImportD = dyn_cast(D)) { - if (auto *Mod = ImportD->getModule()) { - if (Mod->isSwiftShimsModule()) - return true; - } - } - - auto VD = dyn_cast(D); - if (!VD || !VD->hasName()) - return false; - - // If the name has leading underscore then it's a private symbol. - if (!VD->getBaseName().isSpecial() && - VD->getBaseName().getIdentifier().str().startswith("_")) - return true; - - return false; + return hasUnderscoredNaming(); } AvailabilityContext Decl::getAvailabilityForLinkage() const { @@ -2862,6 +2881,16 @@ bool ValueDecl::isImplicitlyUnwrappedOptional() const { false); } +bool ValueDecl::isLocalCapture() const { + auto *dc = getDeclContext(); + + if (auto *fd = dyn_cast(this)) + if (isa(dc)) + return fd->hasTopLevelLocalContextCaptures(); + + return dc->isLocalContext(); +} + ArrayRef ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const { // Dig out the nominal type. @@ -2926,6 +2955,7 @@ Type ValueDecl::getInterfaceType() const { } void ValueDecl::setInterfaceType(Type type) { + assert(!type.isNull() && "Resetting the interface type to null is forbidden"); getASTContext().evaluator.cacheOutput(InterfaceTypeRequest{this}, std::move(type)); } @@ -3529,13 +3559,25 @@ bool NominalTypeDecl::isResilient() const { return getModuleContext()->isResilient(); } +static bool isOriginallyDefinedIn(const Decl *D, const ModuleDecl* MD) { + if (!MD) + return false; + if (D->getAlternateModuleName().empty()) + return false; + return D->getAlternateModuleName() == MD->getName().str(); +} + bool NominalTypeDecl::isResilient(ModuleDecl *M, ResilienceExpansion expansion) const { switch (expansion) { case ResilienceExpansion::Minimal: return isResilient(); case ResilienceExpansion::Maximal: - return M != getModuleContext() && isResilient(); + // We consider this decl belongs to the module either it's currently + // defined in this module or it's originally defined in this module, which + // is specified by @_originallyDefinedIn + return M != getModuleContext() && !isOriginallyDefinedIn(this, M) && + isResilient(); } llvm_unreachable("bad resilience expansion"); } @@ -3638,6 +3680,8 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) { if (!FirstExtension) { FirstExtension = extension; LastExtension = extension; + + addedExtension(extension); return; } @@ -4098,15 +4142,11 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const { return DD; } - bool ClassDecl::hasMissingDesignatedInitializers() const { - if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) { - auto *mutableThis = const_cast(this); - mutableThis->Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); - } - - return Bits.ClassDecl.HasMissingDesignatedInitializers; + return evaluateOrDefault( + getASTContext().evaluator, + HasMissingDesignatedInitializersRequest{const_cast(this)}, + false); } bool ClassDecl::hasMissingVTableEntries() const { @@ -4129,7 +4169,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const { return false; } -bool ClassDecl::inheritsSuperclassInitializers() { +bool ClassDecl::inheritsSuperclassInitializers() const { // If there's no superclass, there's nothing to inherit. if (!getSuperclass()) return false; @@ -4524,7 +4564,7 @@ ProtocolDecl::getInheritedProtocolsSlow() { for (const auto found : getDirectlyInheritedNominalTypeDecls( const_cast(this), anyObject)) { - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.Item)) { if (known.insert(proto).second) result.push_back(proto); } @@ -5233,6 +5273,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, { Bits.VarDecl.Introducer = unsigned(introducer); Bits.VarDecl.IsCaptureList = isCaptureList; + Bits.VarDecl.IsSelfParamCapture = false; Bits.VarDecl.IsDebuggerVar = false; Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; @@ -6023,11 +6064,10 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { type = ParamDecl::getVarargBaseTy(type); auto label = getArgumentName(); - auto flags = ParameterTypeFlags::fromParameterType(type, - isVariadic(), - isAutoClosure(), - isNonEphemeral(), - getValueOwnership()); + auto flags = ParameterTypeFlags::fromParameterType( + type, isVariadic(), isAutoClosure(), isNonEphemeral(), + getValueOwnership(), + /*isNoDerivative*/ false); return AnyFunctionType::Param(type, label, flags); } @@ -6514,7 +6554,7 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { auto &ctx = getASTContext(); auto storage = accessor->getStorage(); auto subscript = dyn_cast(storage); - switch (auto accessorKind = accessor->getAccessorKind()) { + switch (accessor->getAccessorKind()) { // These don't have any extra implicit parameters. case AccessorKind::Address: case AccessorKind::MutableAddress: @@ -6613,6 +6653,20 @@ BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { nullptr); } +void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) { + assert(getBodyKind() != BodyKind::Skipped && + "cannot set a body if it was skipped"); + + Body = S; + setBodyKind(NewBodyKind); + + // Need to recompute init body kind. + if (NewBodyKind < BodyKind::TypeChecked) { + if (auto *ctor = dyn_cast(this)) + ctor->clearCachedDelegatingOrChainedInitKind(); + } +} + SourceRange AbstractFunctionDecl::getBodySourceRange() const { switch (getBodyKind()) { case BodyKind::None: @@ -7389,8 +7443,11 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, // If we already computed the result, return it. if (Bits.ConstructorDecl.ComputedBodyInitKind) { - return static_cast( - Bits.ConstructorDecl.ComputedBodyInitKind - 1); + auto Kind = static_cast( + Bits.ConstructorDecl.ComputedBodyInitKind - 1); + assert((Kind == BodyInitKind::None || !init) && + "can't return cached result with the init expr"); + return Kind; } @@ -7638,6 +7695,12 @@ bool FuncDecl::isPotentialIBActionTarget() const { !isa(this); } +void FuncDecl::setHasTopLevelLocalContextCaptures(bool hasCaptures) { + assert(!hasCaptures || isa(getDeclContext())); + + Bits.FuncDecl.HasTopLevelLocalContextCaptures = hasCaptures; +} + Type TypeBase::getSwiftNewtypeUnderlyingType() { auto structDecl = getStructOrBoundGenericStruct(); if (!structDecl) @@ -7855,3 +7918,10 @@ void ParseAbstractFunctionBodyRequest::cacheResult(BraceStmt *value) const { } } + +void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) { + if (auto func = fn.getAbstractFunctionDecl()) + simple_display(out, func); + else + out << "closure"; +} diff --git a/lib/AST/Evaluator.cpp b/lib/AST/Evaluator.cpp index c73ed3e97c357..f229cd50838d0 100644 --- a/lib/AST/Evaluator.cpp +++ b/lib/AST/Evaluator.cpp @@ -62,8 +62,12 @@ void Evaluator::registerRequestFunctions( requestFunctionsByZone.push_back({zoneID, functions}); } -Evaluator::Evaluator(DiagnosticEngine &diags, bool debugDumpCycles) - : diags(diags), debugDumpCycles(debugDumpCycles) { } +Evaluator::Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph) + : diags(diags), + debugDumpCycles(debugDumpCycles), + buildDependencyGraph(buildDependencyGraph) { } void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { std::error_code error; @@ -72,9 +76,11 @@ void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { } bool Evaluator::checkDependency(const AnyRequest &request) { - // If there is an active request, record it's dependency on this request. - if (!activeRequests.empty()) - dependencies[activeRequests.back()].push_back(request); + if (buildDependencyGraph) { + // If there is an active request, record it's dependency on this request. + if (!activeRequests.empty()) + dependencies[activeRequests.back()].push_back(request); + } // Record this as an active request. if (activeRequests.insert(request)) { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 7eb779525f5cd..72dde8c11666a 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -254,13 +254,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const { SIMPLE_REFERENCE(DeclRef, getDeclRef); SIMPLE_REFERENCE(SuperRef, getSelf); - case ExprKind::Type: { - auto typeRepr = cast(this)->getTypeRepr(); - if (!typeRepr) return ConcreteDeclRef(); - auto ident = dyn_cast(typeRepr); - if (!ident) return ConcreteDeclRef(); - return ident->getComponentRange().back()->getBoundDecl(); - } + NO_REFERENCE(Type); SIMPLE_REFERENCE(OtherConstructorDeclRef, getDeclRef); @@ -324,8 +318,8 @@ ConcreteDeclRef Expr::getReferencedDecl() const { NO_REFERENCE(Binary); NO_REFERENCE(DotSyntaxCall); NO_REFERENCE(MakeTemporarilyEscapable); + NO_REFERENCE(ConstructorRefCall); - PASS_THROUGH_REFERENCE(ConstructorRefCall, getFn); PASS_THROUGH_REFERENCE(Load, getSubExpr); NO_REFERENCE(DestructureTuple); NO_REFERENCE(UnresolvedTypeConversion); @@ -1179,6 +1173,17 @@ UnresolvedSpecializeExpr *UnresolvedSpecializeExpr::create(ASTContext &ctx, UnresolvedParams, RAngleLoc); } +bool CaptureListEntry::isSimpleSelfCapture() const { + if (Init->getPatternList().size() != 1) + return false; + if (auto *DRE = dyn_cast(Init->getInit(0))) + if (auto *VD = dyn_cast(DRE->getDecl())) { + return (VD->isSelfParameter() || VD->isSelfParamCapture()) + && VD->getName() == Var->getName(); + } + return false; +} + CaptureListExpr *CaptureListExpr::create(ASTContext &ctx, ArrayRef captureList, ClosureExpr *closureBody) { @@ -1504,33 +1509,6 @@ DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, hasTrailingClosure, decl, implicit); } -DynamicSubscriptExpr * -DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit) { - SmallVector indexArgLabelsScratch; - SmallVector indexArgLabelLocsScratch; - Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, - indexArgLabelLocs, rSquareLoc, - trailingClosure, implicit, - indexArgLabelsScratch, - indexArgLabelLocsScratch); - - size_t size = totalSizeToAlloc(indexArgLabels, indexArgLabelLocs, - trailingClosure != nullptr); - - void *memory = ctx.Allocate(size, alignof(DynamicSubscriptExpr)); - return new (memory) DynamicSubscriptExpr(base, index, indexArgLabels, - indexArgLabelLocs, - trailingClosure != nullptr, - decl, implicit); -} - UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, DeclNameRef name, Expr *argument, @@ -1840,6 +1818,12 @@ bool ClosureExpr::hasEmptyBody() const { return getBody()->empty(); } +bool ClosureExpr::capturesSelfEnablingImplictSelf() const { + if (auto *VD = getCapturedSelfDecl()) + return VD->isSelfParamCapture() && !VD->getType()->is(); + return false; +} + FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body) void AutoClosureExpr::setBody(Expr *E) { diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 24f7dbb8a3b63..853aaaaade902 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -191,7 +191,7 @@ struct SourceFileDeclFinder { // clang-format off SourceFileDeclFinder(const SourceFile *const SF, const bool includePrivateDecls) : includePrivateDecls(includePrivateDecls) { - for (const Decl *const D : SF->Decls) { + for (const Decl *const D : SF->getTopLevelDecls()) { select(D, extensions, false) || select(D, operators, false) || diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index ad8b0fca4480e..974ddbde530ae 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -1712,22 +1712,6 @@ bool EquivalenceClass::recordConformanceConstraint( return inserted; } -template -bool Constraint::isSubjectEqualTo(Type type) const { - return getSubjectDependentType({ })->isEqual(type); -} - -template -bool Constraint::isSubjectEqualTo(const PotentialArchetype *pa) const { - return getSubjectDependentType({ })->isEqual(pa->getDependentType({ })); -} - -template -bool Constraint::hasSameSubjectAs(const Constraint &other) const { - return getSubjectDependentType({ }) - ->isEqual(other.getSubjectDependentType({ })); -} - Optional EquivalenceClass::findAnyConcreteConstraintAsWritten(Type preferredType) const { // If we don't have a concrete type, there's no source. @@ -3939,13 +3923,13 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( // Local function to find the insertion point for the protocol's "where" // clause, as well as the string to start the insertion ("where" or ","); - auto getProtocolWhereLoc = [&]() -> std::pair { + auto getProtocolWhereLoc = [&]() -> Located { // Already has a trailing where clause. if (auto trailing = proto->getTrailingWhereClause()) - return { trailing->getRequirements().back().getSourceRange().End, ", " }; + return { ", ", trailing->getRequirements().back().getSourceRange().End }; // Inheritance clause. - return { proto->getInherited().back().getSourceRange().End, " where " }; + return { " where ", proto->getInherited().back().getSourceRange().End }; }; // Retrieve the set of requirements that a given associated type declaration @@ -4060,8 +4044,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( assocTypeDecl->getFullName(), inheritedFromProto->getDeclaredInterfaceType()) .fixItInsertAfter( - fixItWhere.first, - getAssociatedTypeReqs(assocTypeDecl, fixItWhere.second)) + fixItWhere.Loc, + getAssociatedTypeReqs(assocTypeDecl, fixItWhere.Item)) .fixItRemove(assocTypeDecl->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, @@ -4136,8 +4120,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( diag::typealias_override_associated_type, name, inheritedFromProto->getDeclaredInterfaceType()) - .fixItInsertAfter(fixItWhere.first, - getConcreteTypeReq(type, fixItWhere.second)) + .fixItInsertAfter(fixItWhere.Loc, + getConcreteTypeReq(type, fixItWhere.Item)) .fixItRemove(type->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, inheritedAssocTypeDecl->getFullName()); diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index 2d8ff08890c96..1b667a75d7373 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -57,7 +57,7 @@ void ImportSet::Profile( for (auto import : topLevelImports) { ID.AddInteger(import.first.size()); for (auto accessPathElt : import.first) { - ID.AddPointer(accessPathElt.first.getAsOpaquePointer()); + ID.AddPointer(accessPathElt.Item.getAsOpaquePointer()); } ID.AddPointer(import.second); } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 486a1e26ba6dd..a478cb81a4514 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -230,7 +230,7 @@ void SourceLookupCache::populateMemberCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "populate-source-file-class-member-cache"); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); MemberCachePopulated = true; } @@ -243,7 +243,7 @@ void SourceLookupCache::populateMemberCache(const ModuleDecl &Mod) { for (const FileUnit *file : Mod.getFiles()) { auto &SF = *cast(file); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); } MemberCachePopulated = true; @@ -275,7 +275,7 @@ void SourceLookupCache::addToMemberCache(Range decls) { SourceLookupCache::SourceLookupCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "source-file-populate-cache"); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { @@ -283,7 +283,7 @@ SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { "module-populate-cache"); for (const FileUnit *file : M.getFiles()) { auto &SF = *cast(file); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } } @@ -303,7 +303,7 @@ void SourceLookupCache::lookupVisibleDecls(AccessPathTy AccessPath, assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); if (!AccessPath.empty()) { - auto I = TopLevelValues.find(AccessPath.front().first); + auto I = TopLevelValues.find(AccessPath.front().Item); if (I == TopLevelValues.end()) return; for (auto vd : I->second) @@ -335,7 +335,7 @@ void SourceLookupCache::lookupClassMembers(AccessPathTy accessPath, for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } @@ -367,7 +367,7 @@ void SourceLookupCache::lookupClassMember(AccessPathTy accessPath, if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().Item) results.push_back(vd); } return; @@ -1181,13 +1181,13 @@ void ModuleDecl::getImportedModulesForLookup( } bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) { - using AccessPathElem = std::pair; + using AccessPathElem = Located; if (lhs.size() != rhs.size()) return false; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first == rElem.first; + return lElem.Item == rElem.Item; }); } @@ -1251,12 +1251,12 @@ ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { lhs.second->getReverseFullModuleName(), {}, rhs.second->getReverseFullModuleName(), {}); } - using AccessPathElem = std::pair; + using AccessPathElem = Located; return std::lexicographical_compare(lhs.first.begin(), lhs.first.end(), rhs.first.begin(), rhs.first.end(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first.str() < rElem.first.str(); + return lElem.Item.str() < rElem.Item.str(); }); }); auto last = std::unique(imports.begin(), imports.end(), diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index afdcc0b40b313..d2d411a8160a3 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -60,6 +60,13 @@ ValueDecl *LookupResultEntry::getBaseDecl() const { return selfDecl; } + if (auto *CE = dyn_cast(BaseDC)) { + auto *selfDecl = CE->getCapturedSelfDecl(); + assert(selfDecl); + assert(selfDecl->isSelfParamCapture()); + return selfDecl; + } + auto *nominalDecl = BaseDC->getSelfNominalTypeDecl(); assert(nominalDecl); return nominalDecl; @@ -873,6 +880,11 @@ class swift::MemberLookupTable { /// Lookup table mapping names to the set of declarations with that name. LookupTable Lookup; + /// The set of names of lazily-loaded members that the lookup table has a + /// complete accounting of with respect to all known extensions of its + /// parent nominal type. + llvm::DenseSet LazilyCompleteNames; + public: /// Create a new member lookup table. explicit MemberLookupTable(ASTContext &ctx); @@ -886,6 +898,24 @@ class swift::MemberLookupTable { /// Add the given members to the lookup table. void addMembers(DeclRange members); + /// Returns \c true if the lookup table has a complete accounting of the + /// given name. + bool isLazilyComplete(DeclBaseName name) const { + return LazilyCompleteNames.find(name) != LazilyCompleteNames.end(); + } + + /// Mark a given lazily-loaded name as being complete. + void markLazilyComplete(DeclBaseName name) { + LazilyCompleteNames.insert(name); + } + + /// Clears the cache of lazily-complete names. This _must_ be called when + /// new extensions with lazy members are added to the type, or direct lookup + /// will return inconsistent or stale results. + void clearLazilyCompleteCache() { + LazilyCompleteNames.clear(); + } + /// Iterator into the lookup table. typedef LookupTable::iterator iterator; @@ -905,7 +935,11 @@ class swift::MemberLookupTable { os << "Lookup:\n "; for (auto &pair : Lookup) { - pair.getFirst().print(os) << ":\n "; + pair.getFirst().print(os); + if (isLazilyComplete(pair.getFirst().getBaseName())) { + os << " (lazily complete)"; + } + os << ":\n "; for (auto &decl : pair.getSecond()) { os << "- "; decl->dumpRef(os); @@ -919,21 +953,6 @@ class swift::MemberLookupTable { dump(llvm::errs()); } - // Mark all Decls in this table as not-resident in a table, drop - // references to them. Should only be called when this was not fully-populated - // from an IterableDeclContext. - void clear() { - // LastExtensionIncluded would only be non-null if this was populated from - // an IterableDeclContext (though it might still be null in that case). - assert(LastExtensionIncluded == nullptr); - for (auto const &i : Lookup) { - for (auto d : i.getSecond()) { - d->setAlreadyInLookupTable(false); - } - } - Lookup.clear(); - } - // Only allow allocation of member lookup tables using the allocator in // ASTContext or by doing a placement new. void *operator new(size_t Bytes, ASTContext &C, @@ -1036,30 +1055,26 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) { } } -void NominalTypeDecl::addedMember(Decl *member) { - auto *vd = dyn_cast(member); - if (!vd) - return; - - // If we have a lookup table, add the new member to it. - auto *lookup = LookupTable.getPointer(); - if (lookup && isLookupTablePopulated()) { - if (hasLazyMembers()) { - // If we have lazy members, only add the new member to the lookup - // table if we already have another member with the same name. - // The presence of a lookup table entry indicates that the - // nominal as well as all extensions have already been searched. - if (lookup->find(vd->getBaseName()) == lookup->end()) - return; - } +void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { + if (!LookupTable) return; - lookup->addMember(vd); + if (ext->hasLazyMembers()) { + LookupTable->addMembers(ext->getCurrentMembersWithoutLoading()); + LookupTable->clearLazilyCompleteCache(); + } else { + LookupTable->addMembers(ext->getMembers()); } } -void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { - if (hasLazyMembers()) - setLookupTablePopulated(false); +void NominalTypeDecl::addedMember(Decl *member) { + // If we have a lookup table, add the new member to it. If not, we'll pick up + // this member when we first create the table. + auto *vd = dyn_cast(member); + auto *lookup = LookupTable; + if (!vd || !lookup) + return; + + lookup->addMember(vd); } void ExtensionDecl::addedMember(Decl *member) { @@ -1092,8 +1107,8 @@ void ExtensionDecl::addedMember(Decl *member) { // │ExtensionDecl *LastExtension ─┼───────┐│ │ └───┐ // │ │ ││ └──────────────────────┐│ // │MemberLookupTable *LookupTable├─┐ ││ ││ -// │bool LookupTableComplete │ │ ││ ┌─────────────────┐ ││ -// └──────────────────────────────┘ │ ││ │ExtensionDecl │ ││ +// └──────────────────────────────┘ │ ││ ┌─────────────────┐ ││ +// │ ││ │ExtensionDecl │ ││ // │ ││ │------------- │ ││ // ┌─────────────┘ │└────▶│ExtensionDecl │ ││ // │ │ │ *NextExtension ├──┐ ││ @@ -1126,16 +1141,13 @@ void ExtensionDecl::addedMember(Decl *member) { // // If the IDC list is later populated and/or an extension is added _after_ // MemberLookupTable is constructed (and possibly has entries in it), -// MemberLookupTable is purged and reconstructed from IDC's list. +// MemberLookupTable is incrementally reconstituted with new members. static bool populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name, IterableDeclContext *IDC) { - if (IDC->isLoadingLazyMembers()) { - return false; - } IDC->setLoadingLazyMembers(true); auto ci = ctx.getOrCreateLazyIterableContextData(IDC, /*lazyLoader=*/nullptr); @@ -1157,82 +1169,66 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, } } -static void populateLookupTableEntryFromCurrentMembers( - ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name, - IterableDeclContext *IDC) { - for (auto m : IDC->getMembers()) { - if (auto v = dyn_cast(m)) { - if (v->getBaseName() == name) { - LookupTable.addMember(m); - } - } - } -} - static void populateLookupTableEntryFromExtensions(ASTContext &ctx, MemberLookupTable &table, NominalTypeDecl *nominal, DeclBaseName name) { + assert(!table.isLazilyComplete(name) && + "Should not be searching extensions for complete name!"); + for (auto e : nominal->getExtensions()) { - if (e->wasDeserialized() || e->hasClangNode()) { - assert(!e->hasUnparsedMembers()); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, - name, e)) { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); - } - } else { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + // If there's no lazy members to look at, all the members of this extension + // are present in the lookup table. + if (!e->hasLazyMembers()) { + continue; } - } -} -bool NominalTypeDecl::isLookupTablePopulated() const { - return LookupTable.getInt(); -} + assert(e->wasDeserialized() || e->hasClangNode() && + "Extension without deserializable content has lazy members!"); + assert(!e->hasUnparsedMembers()); -void NominalTypeDecl::setLookupTablePopulated(bool value) { - LookupTable.setInt(value); + // Try lazy loading. If that fails, then we fall back by loading the + // entire extension. FIXME: It's rather unfortunate that we fall off the + // happy path because the Clang Importer can't handle lazy import-as-member. + if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, name, e)) { + e->loadAllMembers(); + } + } } void NominalTypeDecl::prepareLookupTable() { - // If we haven't allocated the lookup table yet, do so now. - if (!LookupTable.getPointer()) { - auto &ctx = getASTContext(); - LookupTable.setPointer(new (ctx) MemberLookupTable(ctx)); + // If we have already allocated the lookup table, then there's nothing further + // to do. + if (LookupTable) { + return; } + // Otherwise start the first fill. + auto &ctx = getASTContext(); + LookupTable = new (ctx) MemberLookupTable(ctx); + if (hasLazyMembers()) { assert(!hasUnparsedMembers()); // Lazy members: if the table needs population, populate the table _only // from those members already in the IDC member list_ such as implicits or - // globals-as-members, then update table entries from the extensions that - // have the same names as any such initial-population members. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getCurrentMembersWithoutLoading()); - - llvm::SetVector baseNamesPresent; - for (auto entry : *LookupTable.getPointer()) { - baseNamesPresent.insert(entry.getFirst().getBaseName()); + // globals-as-members. + LookupTable->addMembers(getCurrentMembersWithoutLoading()); + for (auto e : getExtensions()) { + // If we can lazy-load this extension, only take the members we've loaded + // so far. + if (e->wasDeserialized() || e->hasClangNode()) { + LookupTable->addMembers(e->getCurrentMembersWithoutLoading()); + continue; } - for (auto baseName : baseNamesPresent) { - populateLookupTableEntryFromExtensions(getASTContext(), - *LookupTable.getPointer(), - this, baseName); - } + // Else, load all the members into the table. + LookupTable->addMembers(e->getMembers()); } - } else { - // No lazy members: if the table needs population, populate the table - // en-masse; and in either case update the extensions. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getMembers()); - } - LookupTable.getPointer()->updateLookupTable(this); + LookupTable->addMembers(getMembers()); + LookupTable->updateLookupTable(this); } } @@ -1258,9 +1254,9 @@ maybeFilterOutAttrImplements(TinyPtrVector decls, return result; } -TinyPtrVector NominalTypeDecl::lookupDirect( - DeclName name, - OptionSet flags) { +TinyPtrVector +NominalTypeDecl::lookupDirect(DeclName name, + OptionSet flags) { ASTContext &ctx = getASTContext(); if (auto s = ctx.Stats) { ++s->getFrontendCounters().NominalTypeLookupDirectCount; @@ -1268,93 +1264,66 @@ TinyPtrVector NominalTypeDecl::lookupDirect( // We only use NamedLazyMemberLoading when a user opts-in and we have // not yet loaded all the members into the IDC list in the first place. - bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && - hasLazyMembers()); + const bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && + hasLazyMembers()); - bool includeAttrImplements = + const bool includeAttrImplements = flags.contains(LookupDirectFlags::IncludeAttrImplements); - // FIXME: At present, lazy member is not able to find inherited constructors - // in imported classes, because SwiftDeclConverter::importInheritedConstructors() - // is only called via ClangImporter::Implementation::loadAllMembers(). - if (hasClangNode() && - name.getBaseName() == DeclBaseName::createConstructor()) - useNamedLazyMemberLoading = false; - LLVM_DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" - << name << ")" - << ", isLookupTablePopulated()=" << isLookupTablePopulated() - << ", hasLazyMembers()=" << hasLazyMembers() - << ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading - << "\n"); - - // We check the LookupTable at most twice, possibly treating a miss in the - // first try as a cache-miss that we then do a cache-fill on, and retry. - for (int i = 0; i < 2; ++i) { - - // First, if we're _not_ doing NamedLazyMemberLoading, we make sure we've - // populated the IDC and brought it up to date with any extensions. This - // will flip the hasLazyMembers() flag to false as well. - if (!useNamedLazyMemberLoading) { - // It's possible that the lookup table exists but has information in it - // that is either currently out of date or soon to be out of date. - // This can happen two ways: - // - // - We've not yet indexed the members we have (isLookupTablePopulated() - // is zero). - // - // - We've still got more lazy members left to load; this can happen - // even if we _did_ index some members. - // - // In either of these cases, we want to reset the table to empty and - // mark it as needing reconstruction. - if (LookupTable.getPointer() && - (hasLazyMembers() || !isLookupTablePopulated())) { - LookupTable.getPointer()->clear(); - setLookupTablePopulated(false); - } - - (void)getMembers(); + << name << ")" + << ", hasLazyMembers()=" << hasLazyMembers() + << ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading + << "\n"); - // Make sure we have the complete list of members (in this nominal and in - // all extensions). - for (auto E : getExtensions()) - (void)E->getMembers(); - } - // Next, in all cases, prepare the lookup table for use, possibly - // repopulating it from the IDC if the IDC member list has just grown. - prepareLookupTable(); + prepareLookupTable(); + auto tryCacheLookup = + [=](MemberLookupTable *table, + DeclName name) -> Optional> { // Look for a declaration with this name. - auto known = LookupTable.getPointer()->find(name); + auto known = table->find(name); + if (known == table->end()) { + return None; + } // We found something; return it. - if (known != LookupTable.getPointer()->end()) - return maybeFilterOutAttrImplements(known->second, name, - includeAttrImplements); + return maybeFilterOutAttrImplements(known->second, name, + includeAttrImplements); + }; - // If we have no more second chances, stop now. - if (!useNamedLazyMemberLoading || i > 0) - break; + auto updateLookupTable = [this](MemberLookupTable *table) { + // Make sure we have the complete list of members (in this nominal and in + // all extensions). + (void)getMembers(); + + for (auto E : getExtensions()) + (void)E->getMembers(); + + LookupTable->updateLookupTable(this); + }; - // If we get here, we had a cache-miss and _are_ using - // NamedLazyMemberLoading. Try to populate a _single_ entry in the - // MemberLookupTable from both this nominal and all of its extensions, and - // retry. Any failure to load here flips the useNamedLazyMemberLoading to - // false, and we fall back to loading all members during the retry. - auto &Table = *LookupTable.getPointer(); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, - name.getBaseName(), this)) { - useNamedLazyMemberLoading = false; + if (!useNamedLazyMemberLoading) { + updateLookupTable(LookupTable); + } else if (!LookupTable->isLazilyComplete(name.getBaseName())) { + // The lookup table believes it doesn't have a complete accounting of this + // name - either because we're never seen it before, or another extension + // was registered since the last time we searched. Ask the loaders to give + // us a hand. + auto &Table = *LookupTable; + DeclBaseName baseName(name.getBaseName()); + if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, baseName, this)) { + updateLookupTable(LookupTable); } else { - populateLookupTableEntryFromExtensions(ctx, Table, this, - name.getBaseName()); + populateLookupTableEntryFromExtensions(ctx, Table, this, baseName); } + Table.markLazilyComplete(baseName); } - // None of our attempts found anything. - return { }; + // Look for a declaration with this name. + return tryCacheLookup(LookupTable, name) + .getValueOr(TinyPtrVector()); } void ClassDecl::createObjCMethodLookup() { @@ -1587,17 +1556,24 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); + QualifiedLookupRequest req{this, {typeDecls.begin(), typeDecls.end()}, + member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto *stats = getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInNominal++; +llvm::Expected +QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + SmallVector typeDecls, + DeclNameRef member, NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); // Tracking for the nominal types we'll visit. SmallVector stack; @@ -1645,8 +1621,7 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, if ((options & NL_OnlyTypes) && !isa(decl)) continue; - if (isAcceptableLookupResult(this, options, decl, - onlyCompleteObjectInits)) + if (isAcceptableLookupResult(DC, options, decl, onlyCompleteObjectInits)) decls.push_back(decl); } @@ -1706,34 +1681,40 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, } } - pruneLookupResultSet(this, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInNominals(this, typeDecls, member.getFullName(), + pruneLookupResultSet(DC, options, decls); + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInNominals(DC, typeDecls, member.getFullName(), options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; + assert(decls.empty() && "additive lookup not supported"); + ModuleQualifiedLookupRequest req{this, module, member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto &ctx = getASTContext(); - auto *stats = ctx.Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInModule++; +llvm::Expected +ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + ModuleDecl *module, DeclNameRef member, + NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); auto kind = (options & NL_OnlyTypes ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable); - auto topLevelScope = getModuleScopeContext(); + auto topLevelScope = DC->getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { if (tracker) { recordLookupOfTopLevelName(topLevelScope, member.getFullName(), @@ -1748,6 +1729,7 @@ bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, // anything in this one. // Perform the lookup in all imports of this module. + auto &ctx = DC->getASTContext(); auto accessPaths = ctx.getImportCache().getAllVisibleAccessPaths( module, topLevelScope); if (llvm::any_of(accessPaths, @@ -1760,14 +1742,14 @@ bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, } } - pruneLookupResultSet(this, options, decls); + pruneLookupResultSet(DC, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInModule(this, module, member.getFullName(), + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInModule(DC, module, member.getFullName(), options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } llvm::Expected @@ -1997,7 +1979,9 @@ directReferencesForQualifiedTypeLookup(Evaluator &evaluator, auto innerOptions = options; innerOptions &= ~NL_RemoveOverridden; innerOptions &= ~NL_RemoveNonVisible; - dc->lookupQualified(module, name, innerOptions, members); + SmallVector moduleMembers; + dc->lookupQualified(module, name, innerOptions, moduleMembers); + members.append(moduleMembers.begin(), moduleMembers.end()); } addResults(members); @@ -2349,7 +2333,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, void swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject) { auto typeDecl = decl.dyn_cast(); auto extDecl = decl.dyn_cast(); @@ -2378,11 +2362,11 @@ void swift::getDirectlyInheritedNominalTypeDecls( // Form the result. for (auto nominal : nominalTypes) { - result.push_back({loc, nominal}); + result.push_back({nominal, loc}); } } -SmallVector, 4> +SmallVector, 4> swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject) { @@ -2392,7 +2376,7 @@ swift::getDirectlyInheritedNominalTypeDecls( // Gather results from all of the inherited types. unsigned numInherited = typeDecl ? typeDecl->getInherited().size() : extDecl->getInherited().size(); - SmallVector, 4> result; + SmallVector, 4> result; for (unsigned i : range(numInherited)) { getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject); } @@ -2418,8 +2402,8 @@ swift::getDirectlyInheritedNominalTypeDecls( if (!req.getFirstType()->isEqual(protoSelfTy)) continue; - result.emplace_back( - loc, req.getSecondType()->castTo()->getDecl()); + result.emplace_back(req.getSecondType()->castTo()->getDecl(), + loc); } return result; } @@ -2429,7 +2413,7 @@ swift::getDirectlyInheritedNominalTypeDecls( anyObject |= selfBounds.anyObject; for (auto inheritedNominal : selfBounds.decls) - result.emplace_back(loc, inheritedNominal); + result.emplace_back(inheritedNominal, loc); return result; } @@ -2486,7 +2470,7 @@ void FindLocalVal::checkGenericParams(GenericParamList *Params) { } void FindLocalVal::checkSourceFile(const SourceFile &SF) { - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) if (auto *TLCD = dyn_cast(D)) visitBraceStmt(TLCD->getBody(), /*isTopLevel=*/true); } @@ -2523,7 +2507,8 @@ void FindLocalVal::visitGuardStmt(GuardStmt *S) { return; // Names in the guard aren't visible until after the body. - if (!isReferencePointInRange(S->getBody()->getSourceRange())) + if (S->getBody()->isImplicit() || + !isReferencePointInRange(S->getBody()->getSourceRange())) checkStmtCondition(S->getCond()); visit(S->getBody()); diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 5eb06e0bafa6d..d6013f2c10bc0 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -67,6 +67,47 @@ void SuperclassDeclRequest::cacheResult(ClassDecl *value) const { protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true); } +//----------------------------------------------------------------------------// +// Missing designated initializers computation +//----------------------------------------------------------------------------// + +Optional HasMissingDesignatedInitializersRequest::getCachedResult() const { + auto classDecl = std::get<0>(getStorage()); + return classDecl->getCachedHasMissingDesignatedInitializers(); +} + +void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const { + auto classDecl = std::get<0>(getStorage()); + classDecl->setHasMissingDesignatedInitializers(result); +} + +llvm::Expected +HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator, + ClassDecl *subject) const { + // Short-circuit and check for the attribute here. + if (subject->getAttrs().hasAttribute()) + return true; + + AccessScope scope = + subject->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + // This flag only makes sense for public types that will be written in the + // module. + if (!scope.isPublic()) + return false; + + auto constructors = subject->lookupDirect(DeclBaseName::createConstructor()); + return llvm::any_of(constructors, [&](ValueDecl *decl) { + auto init = cast(decl); + if (!init->isDesignatedInit()) + return false; + AccessScope scope = + init->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + return !scope.isPublic(); + }); +} + //----------------------------------------------------------------------------// // Extended nominal computation. //----------------------------------------------------------------------------// diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index 447ba7fe0abbd..9f2924ac2945c 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -496,3 +496,33 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + + +ContextualPattern ContextualPattern::forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index) { + return ContextualPattern( + pbd->getPattern(index), /*isTopLevel=*/true, pbd, index); +} + +DeclContext *ContextualPattern::getDeclContext() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->getDeclContext(); + + return declOrContext.get(); +} + +PatternBindingDecl *ContextualPattern::getPatternBindingDecl() const { + return declOrContext.dyn_cast(); +} + +bool ContextualPattern::allowsInference() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->isInitialized(index); + + return true; +} + +void swift::simple_display(llvm::raw_ostream &out, + const ContextualPattern &pattern) { + out << "(pattern @ " << pattern.getPattern() << ")"; +} diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index d0c3456fe30c9..0baad09194358 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -200,24 +200,12 @@ void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, #define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&ProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&ProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ - static_assert(&ProtocolConformance::Method != \ - &SpecializedProtocolConformance::Method, \ - "Must override SpecializedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ - static_assert(&ProtocolConformance::Method != \ - &InheritedProtocolConformance::Method, \ - "Must override InheritedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -225,14 +213,8 @@ llvm_unreachable("bad ProtocolConformanceKind"); #define ROOT_CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&RootProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&RootProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d63b809756ac4..00486319c22b1 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Types.h" #include "ForeignRepresentationInfo.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ReferenceCounting.h" #include "swift/AST/TypeCheckRequests.h" @@ -847,19 +848,14 @@ ParameterListInfo::ParameterListInfo( return; } - switch (params.size()) { - case 0: + if (params.empty()) return; - default: - // Arguments and parameters are not guaranteed to always line-up - // perfectly, e.g. failure diagnostics tries to match argument type - // to different "candidate" parameters. - if (params.size() != paramList->size()) - return; - - break; - } + // Arguments and parameters are not guaranteed to always line-up + // perfectly, e.g. failure diagnostics tries to match argument type + // to different "candidate" parameters. + if (params.size() != paramList->size()) + return; // Note which parameters have default arguments and/or function builders. for (auto i : range(0, params.size())) { @@ -3239,6 +3235,11 @@ Type ProtocolCompositionType::get(const ASTContext &C, return build(C, CanTypes, HasExplicitAnyObject); } +void AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) { + cml->printClangType(ClangFunctionType, os); +} + void AnyFunctionType::ExtInfo::assertIsFunctionType(const clang::Type *type) { #ifndef NDEBUG diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 200f3aa5604e4..a1fd18d5af19b 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -298,6 +298,8 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printSimpleAttr("@autoclosure") << " "; if (hasAttr(TAK_escaping)) Printer.printSimpleAttr("@escaping") << " "; + if (hasAttr(TAK_noDerivative)) + Printer.printSimpleAttr("@noDerivative") << " "; if (hasAttr(TAK_differentiable)) { if (Attrs.isLinear()) { @@ -423,7 +425,7 @@ TupleTypeRepr::TupleTypeRepr(ArrayRef Elements, // Set ellipsis location and index. if (Ellipsis.isValid()) { - getTrailingObjects()[0] = {Ellipsis, EllipsisIdx}; + getTrailingObjects()[0] = {EllipsisIdx, Ellipsis}; } } diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 4e62f84f4cf9a..3c7a152b92d59 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -196,7 +196,7 @@ namespace { SourceFile const *recordedSF = nullptr; bool recordedIsCascadingUse = false; unsigned resultsSizeBeforeLocalsPass = ~0; - + public: // clang-format off UnqualifiedLookupFactory(DeclNameRef Name, @@ -252,52 +252,70 @@ namespace { void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse); - void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse); + /// When performing a lookup, we may come across a capture of 'self'. We + /// will need to remember the DeclContext of the innermost captured self so + /// that it can be used as the base DeclContext if we find a lookup result + /// in the enclosing type. \c capturedSelfContext tracks this. + void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse, + DeclContext *capturedSelfContext); void finishLookingInContext( AddGenericParameters addGenericParameters, DeclContext *lookupContextForThisContext, Optional &&resultFinderForTypeContext, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupInModuleScopeContext(DeclContext *, Optional isCascadingUse); void lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PatternBindingInitializer *PBI, Optional isCascadingUse); /// An initializer of a global name, or a function-likelocal name. void lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); template void lookupNamesIntroducedByNominalTypeOrExtension( NominalTypeDeclOrExtensionDecl *D, Optional isCascadingUse); void lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse); + DefaultArgumentInitializer *I, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMiscContext(DeclContext *dc, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookForLocalVariablesIn(AbstractFunctionDecl *AFD, Optional isCascadingUse); @@ -481,7 +499,7 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { if (Name.isOperator()) lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - lookupNamesIntroducedBy(contextAndIsCascadingUse); + lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); } if (crosscheckUnqualifiedLookup && @@ -495,7 +513,7 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { else if (Name.isOperator()) altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse); + altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); const auto *ASTScopeLabel = "ASTScope lookup"; const auto *contextLabel = "context-bsed lookup"; @@ -556,28 +574,43 @@ void UnqualifiedLookupFactory::lookupOperatorInDeclContexts( // TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl void UnqualifiedLookupFactory::lookupNamesIntroducedBy( - const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg) { + const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook; const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse; - if (dc->isModuleScopeContext()) + if (dc->isModuleScopeContext()) { + assert(capturedSelfContext == NULL && "By the time we reach module scope," + " there should be no 'self'."); lookupInModuleScopeContext(dc, isCascadingUseSoFar); + } else if (auto *PBI = dyn_cast(dc)) - lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar); + lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar, + capturedSelfContext); else if (auto *AFD = dyn_cast(dc)) - lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar); + lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar, + capturedSelfContext); else if (auto *ACE = dyn_cast(dc)) - lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar); - else if (auto *ED = dyn_cast(dc)) + lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar, + capturedSelfContext); + else if (auto *ED = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar); - else if (auto *ND = dyn_cast(dc)) + } + else if (auto *ND = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar); + } else if (auto I = dyn_cast(dc)) - lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar); + lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar, + capturedSelfContext); else - lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar); + lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupInModuleScopeContext( @@ -595,22 +628,29 @@ void UnqualifiedLookupFactory::lookupInModuleScopeContext( } void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // Lazy variable initializer contexts have a 'self' parameter for // instance member lookup. if (auto *selfParam = PBI->getImplicitSelfDecl()) lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam, - isCascadingUse); - else if (PBI->getParent()->isTypeContext()) - lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(PBI, + isCascadingUse, + capturedSelfContext); + else if (PBI->getParent()->isTypeContext()) { + assert(capturedSelfContext == NULL && "If we were in a type's property" + " initializer, there should be no 'self' to have been captured."); + lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( + PBI, isCascadingUse); + } else - lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse); + lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer( PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse) { + Optional isCascadingUse, DeclContext *capturedSelfContext) { Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); ifNotDoneYet([&] { DeclContext *const patternContainer = PBI->getParent(); @@ -620,7 +660,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( patternContainer, ResultFinderForTypeContext(this, PBI, patternContainer), resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -638,13 +679,15 @@ void UnqualifiedLookupFactory:: ResultFinderForTypeContext( this, storedPropertyContainer, storedPropertyContainer), resolveIsCascadingUse(storedPropertyContainer, None, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // There's not much to find here, we'll keep going up to a parent // context. // clang-format off @@ -653,12 +696,14 @@ void UnqualifiedLookupFactory:: PBI, None, // not looking in the partic type resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( - AbstractFunctionDecl *AFD, Optional isCascadingUseArg) { + AbstractFunctionDecl *AFD, Optional isCascadingUseArg, + DeclContext *capturedSelfContext) { // DOUG: how does this differ from isOutsideBodyOfFunction below? const bool isCascadingUse = @@ -668,13 +713,16 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc))); if (AFD->getDeclContext()->isTypeContext()) - lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse); + lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse, + capturedSelfContext); else - lookupNamesIntroducedByPureFunction(AFD, isCascadingUse); + lookupNamesIntroducedByPureFunction(AFD, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet( [&] { @@ -690,9 +738,13 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( // If we're not in the body of the function (for example, we // might be type checking a default argument expression and // performing name lookup from there), the base declaration - // is the nominal type, not 'self'. + // is the nominal type, not 'self'. If we've captured self + // somewhere down the tree, we should use that as the context + // for lookup. DeclContext *const BaseDC = - isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD; + isOutsideBodyOfFunction(AFD) ? fnDeclContext + : capturedSelfContext ? capturedSelfContext + : AFD; // If we are inside of a method, check to see if there are any ivars in // scope, and if so, whether this is a reference to one of them. // FIXME: We should persist this information between lookups. @@ -701,13 +753,15 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( AddGenericParameters::Yes, AFD->getParent(), ResultFinderForTypeContext(this, BaseDC, fnDeclContext), - isCascadingUse); + isCascadingUse, + NULL); // clang-format on }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet([&] { // clang-format off @@ -715,15 +769,24 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( AddGenericParameters::Yes, AFD, None, - isCascadingUse); + isCascadingUse, + capturedSelfContext); }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( - AbstractClosureExpr *ACE, Optional isCascadingUse) { - if (auto *CE = dyn_cast(ACE)) + AbstractClosureExpr *ACE, Optional isCascadingUse, + DeclContext *capturedSelfContext) { + if (auto *CE = dyn_cast(ACE)) { lookForLocalVariablesIn(CE); + // If we don't already have a captured self context, and this closure + // captures the self param (not weakly, so that implicit self is available), + // remember that. + if (capturedSelfContext == nullptr) + if (CE->capturesSelfEnablingImplictSelf()) + capturedSelfContext = CE; + } ifNotDoneYet([&] { // clang-format off finishLookingInContext( @@ -731,7 +794,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( ACE, None, resolveIsCascadingUse(ACE, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -748,21 +812,25 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension( ResultFinderForTypeContext(this, D, D)) : None, resolveIsCascadingUse(D, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse) { + DefaultArgumentInitializer *I, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // In a default argument, skip immediately out of both the // initializer and the function. - finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false); + finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( - DeclContext *dc, Optional isCascadingUse) { + DeclContext *dc, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // clang-format off assert(isa(dc) || isa(dc) || @@ -774,7 +842,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( dc, None, resolveIsCascadingUse(DC, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } @@ -783,7 +852,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( const AddGenericParameters addGenericParameters, DeclContext *const lookupContextForThisContext, Optional &&resultFinderForTypeContext, - const Optional isCascadingUse) { + const Optional isCascadingUse, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif @@ -804,7 +874,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( // Recurse into the next context. [&] { lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{ - lookupContextForThisContext->getParentForLookup(), isCascadingUse}); + lookupContextForThisContext->getParentForLookup(), isCascadingUse}, + capturedSelfContext); }); } diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 8504b672afc70..fd60da8ff5379 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,59 +11,32 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -# Figure out if we can track VC revisions. -# FIXME: Copied from Clang. -function(find_first_existing_file out_var) - foreach(file ${ARGN}) - if(EXISTS "${file}") - set(${out_var} "${file}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file("${dir}" ${name}_vc) -macro(find_first_existing_vc_file out_var path) - find_first_existing_file(${out_var} - "${path}/.git/logs/HEAD" # Git - "${path}/.svn/wc.db" # SVN 1.7 - "${path}/.svn/entries" # SVN 1.6 - ) -endmacro() + # Create custom target to generate the VC revision include. + set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") -set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") + set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file(dep_file "${dir}") - # Create custom target to generate the VC revision include. - set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") - string(TOUPPER ${name} upper_name) - if(DEFINED dep_file) - add_custom_command(OUTPUT "${revision_inc}" - DEPENDS "${dep_file}" "${generate_vcs_version_script}" - COMMAND - ${CMAKE_COMMAND} "-DNAMES=${upper_name}" - "-D${upper_name}_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${revision_inc}" - -P "${generate_vcs_version_script}") - else() - # Generate an empty Revision.inc file if we are not using git or SVN. - file(WRITE "${revision_inc}" "") - endif() + add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=$" + "-D$_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") # Mark the generated header as being generated. - set_source_files_properties("${revision_inc}" + set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) + set(${revision_inc_var} ${version_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") -set(version_inc_files - ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) - add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -77,6 +50,7 @@ add_swift_host_library(swiftBasic STATIC JSONSerialization.cpp LangOptions.cpp LLVMContext.cpp + Located.cpp Mangler.cpp OutputFileMap.cpp Platform.cpp @@ -94,7 +68,10 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - ${version_inc_files} + + ${llvm_revision_inc} + ${clang_revision_inc} + ${swift_revision_inc} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc diff --git a/lib/Basic/Located.cpp b/lib/Basic/Located.cpp new file mode 100644 index 0000000000000..0c33f1a14e1e0 --- /dev/null +++ b/lib/Basic/Located.cpp @@ -0,0 +1,28 @@ +//===--- Located.cpp - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +#include "llvm/Support/raw_ostream.h" +#include "swift/Basic/Located.h" + +using namespace swift; + +template +void Located::dump() const { + dump(llvm::errs()); +} + +template +void Located::dump(raw_ostream &os) const { + // FIXME: The following does not compile on newer clangs because operator<< + // does not exist for SourceLoc. More so, the operator does not exist because + // one needs a SourceManager reference and buffer ID to convert any given + // SourceLoc into line and column information. + //os << Loc << " " << Item; +} diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 06bc996822217..85abc0ee97e43 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -342,6 +342,20 @@ llvm::Triple swift::getTargetSpecificModuleTriple(const llvm::Triple &triple) { return triple; } +llvm::Triple swift::getUnversionedTriple(const llvm::Triple &triple) { + StringRef unversionedOSName = triple.getOSName().take_until(llvm::isDigit); + if (triple.getEnvironment()) { + StringRef environment = + llvm::Triple::getEnvironmentTypeName(triple.getEnvironment()); + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName, environment); + } + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName); +} + Optional swift::getSwiftRuntimeCompatibilityVersionForTarget( const llvm::Triple &Triple) { diff --git a/lib/Basic/PrefixMap.cpp b/lib/Basic/PrefixMap.cpp index 3fe43ad6cf7cd..9f7390d698cce 100644 --- a/lib/Basic/PrefixMap.cpp +++ b/lib/Basic/PrefixMap.cpp @@ -34,8 +34,8 @@ enum class ChildKind { Left, Right, Further, Root }; // that's technically instantiation-specific. Redefining the struct here // is technically an aliasing violation, but we can just tell the compilers // that actually use TBAA that this is okay. -typedef struct _Node Node LLVM_MAY_ALIAS; -struct _Node { +typedef struct _Node Node; +struct LLVM_MAY_ALIAS _Node { // If you change the layout in the header, you'll need to change it here. // (This comment is repeated there.) Node *Left, *Right, *Further; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b06020ecb4f44..e3a21225fdb66 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(SwiftRemoteMirror) add_subdirectory(SIL) add_subdirectory(SILGen) add_subdirectory(SILOptimizer) +add_subdirectory(SymbolGraphGen) add_subdirectory(Syntax) add_subdirectory(SyntaxParse) add_subdirectory(TBDGen) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 667499a9640eb..d2457e642fe71 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -598,6 +598,10 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, }); } + if (triple.isOSWASI()) { + invocationArgStrs.insert(invocationArgStrs.end(), {"-D_WASI_EMULATED_MMAN"}); + } + if (triple.isOSWindows()) { switch (triple.getArch()) { default: llvm_unreachable("unsupported Windows architecture"); @@ -1076,10 +1080,6 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, // read them later. instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; - // Make sure to not trigger extra rebuilds on identical files with mismatching - // timestamps. - instance.getHeaderSearchOpts().ValidateASTInputFilesContent = true; - if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule) return importer; @@ -1623,19 +1623,19 @@ void ClangImporter::collectVisibleTopLevelModuleNames( } void ClangImporter::collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const { auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return; clang::Module *submodule = clangModule; for (auto component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.Item.str()); if (!submodule) return; } @@ -1647,12 +1647,12 @@ bool ClangImporter::isModuleImported(const clang::Module *M) { return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; } -bool ClangImporter::canImportModule(std::pair moduleID) { +bool ClangImporter::canImportModule(Located moduleID) { // Look up the top-level module to see if it exists. // FIXME: This only works with top-level modules. auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); clang::Module *clangModule = - clangHeaderSearch.lookupModule(moduleID.first.str(), /*AllowSearch=*/true, + clangHeaderSearch.lookupModule(moduleID.Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) { return false; @@ -1666,13 +1666,13 @@ bool ClangImporter::canImportModule(std::pair moduleID) { } ModuleDecl *ClangImporter::Implementation::loadModuleClang( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { auto &clangContext = getClangASTContext(); auto &clangHeaderSearch = getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first, to see if it exists at all. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return nullptr; @@ -1681,8 +1681,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( SmallVector, 4> clangPath; for (auto component : path) { - clangPath.push_back({&clangContext.Idents.get(component.first.str()), - exportSourceLoc(component.second)}); + clangPath.push_back({&clangContext.Idents.get(component.Item.str()), + exportSourceLoc(component.Loc)}); } auto &rawDiagClient = Instance->getDiagnosticClient(); @@ -1732,13 +1732,13 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // Verify that the submodule exists. clang::Module *submodule = clangModule; for (auto &component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.Item.str()); // Special case: a submodule named "Foo.Private" can be moved to a top-level // module named "Foo_Private". Clang has special support for this. // We're limiting this to just submodules named "Private" because this will // put the Clang AST in a fatal error state if it /doesn't/ exist. - if (!submodule && component.first.str() == "Private" && + if (!submodule && component.Item.str() == "Private" && (&component) == (&path[1])) { submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); } @@ -1757,18 +1757,24 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( return finishLoadingClangModule(clangModule, /*preferOverlay=*/false); } -ModuleDecl *ClangImporter::loadModule( - SourceLoc importLoc, - ArrayRef> path) { - ModuleDecl *MD = Impl.loadModuleClang(importLoc, path); +ModuleDecl * +ClangImporter::loadModule(SourceLoc importLoc, + ArrayRef> path) { + return Impl.loadModule(importLoc, path); +} + +ModuleDecl *ClangImporter::Implementation::loadModule( + SourceLoc importLoc, ArrayRef> path) { + ModuleDecl *MD = nullptr; + if (!DisableSourceImport) + MD = loadModuleClang(importLoc, path); if (!MD) - MD = Impl.loadModuleDWARF(importLoc, path); + MD = loadModuleDWARF(importLoc, path); return MD; } ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule( - const clang::Module *clangModule, - bool findOverlay) { + const clang::Module *clangModule, bool findOverlay) { assert(clangModule); // Bump the generation count. @@ -1954,6 +1960,7 @@ ClangImporter::Implementation::Implementation( BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)), BuffersForDiagnostics(ctx.SourceMgr), platformAvailability(ctx.LangOpts), nameImporter(), + DisableSourceImport(opts.DisableSourceImport), DWARFImporter(dwarfImporterDelegate) {} ClangImporter::Implementation::~Implementation() { @@ -2613,7 +2620,8 @@ void ClangImporter::lookupTypeDecl( clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(), lookupKind); bool foundViaClang = false; - if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { + if (!Impl.DisableSourceImport && + sema.LookupName(lookupResult, /*Scope=*/nullptr)) { for (auto clangDecl : lookupResult) { if (!isa(clangDecl) && !isa(clangDecl) && @@ -2782,7 +2790,7 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx, ArrayRef Exported) { auto *ImportedMod = ClangN.getClangModule(); assert(ImportedMod); - SmallVector, 4> AccessPath; + SmallVector, 4> AccessPath; auto *TmpMod = ImportedMod; while (TmpMod) { AccessPath.push_back({ Ctx.getIdentifier(TmpMod->Name), SourceLoc() }); @@ -3006,7 +3014,7 @@ void ClangImporter::loadExtensions(NominalTypeDecl *nominal, // FIXME: If we already looked at this for this generation, // skip. - for (auto entry : table.lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table.allGlobalsAsMembersInContext(effectiveClangContext)) { // If the entry is not visible, skip it. if (!isVisibleClangEntry(clangCtx, entry)) continue; @@ -3271,6 +3279,29 @@ void ClangImporter::verifyAllModules() { #endif } +const clang::Type * +ClangImporter::parseClangFunctionType(StringRef typeStr, + SourceLoc loc) const { + auto &sema = Impl.getClangSema(); + StringRef filename = Impl.SwiftContext.SourceMgr.getDisplayNameForLoc(loc); + // TODO: Obtain a clang::SourceLocation from the swift::SourceLoc we have + auto parsedType = sema.ParseTypeFromStringCallback(typeStr, filename, {}); + if (!parsedType.isUsable()) + return nullptr; + clang::QualType resultType = clang::Sema::GetTypeFromParser(parsedType.get()); + auto *typePtr = resultType.getTypePtrOrNull(); + if (typePtr && (typePtr->isFunctionPointerType() + || typePtr->isBlockPointerType())) + return typePtr; + return nullptr; +} + +void ClangImporter::printClangType(const clang::Type *type, + llvm::raw_ostream &os) const { + auto policy = clang::PrintingPolicy(getClangASTContext().getLangOpts()); + clang::QualType(type, 0).print(os, policy); +} + //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// @@ -3759,17 +3790,6 @@ ClangImporter::Implementation::loadNamedMembers( return None; } - // Also bail out if there are any global-as-member mappings for this context; - // we can support some of them lazily but the full set of idioms seems - // prohibitively complex (also they're not stored in by-name lookup, for - // reasons unclear). - if (isa(D) && !checkedGlobalsAsMembers.insert(IDC).second) { - if (forEachLookupTable([&](SwiftLookupTable &table) -> bool { - return (!table.lookupGlobalsAsMembers(effectiveClangContext).empty()); - })) - return None; - } - // There are 3 cases: // // - The decl is from a bridging header, CMO is Some(nullptr) @@ -3818,6 +3838,37 @@ ClangImporter::Implementation::loadNamedMembers( } } } + + for (auto entry : table->lookupGlobalsAsMembers(SerializedSwiftName(N), + effectiveClangContext)) { + if (!entry.is()) continue; + auto member = entry.get(); + if (!isVisibleClangEntry(clangCtx, member)) continue; + + // Skip Decls from different clang::DeclContexts + if (member->getDeclContext() != CDC) continue; + + SmallVector tmp; + insertMembersAndAlternates(member, tmp); + for (auto *TD : tmp) { + if (auto *V = dyn_cast(TD)) { + // Skip ValueDecls if they import under different names. + if (V->getBaseName() == N) { + Members.push_back(V); + } + } + } + } + + if (N == DeclBaseName::createConstructor()) { + if (auto *classDecl = dyn_cast(D)) { + SmallVector ctors; + importInheritedConstructors(cast(CD), + classDecl, ctors); + for (auto ctor : ctors) + Members.push_back(cast(ctor)); + } + } return Members; } diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 654fd287d9ce4..d044355cb0d10 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -99,13 +99,13 @@ static_assert(IsTriviallyDestructible::value, "DWARFModuleUnits are BumpPtrAllocated; the d'tor is not called"); ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { // There's no importing from debug info if no importer is installed. if (!DWARFImporter) return nullptr; // FIXME: Implement submodule support! - Identifier name = path[0].first; + Identifier name = path[0].Item; auto it = DWARFModuleUnits.find(name); if (it != DWARFModuleUnits.end()) return it->second->getParentModule(); @@ -128,6 +128,17 @@ ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( return decl; } +// This function exists to defeat the lazy member importing mechanism. The +// DWARFImporter is not capable of loading individual members, so it cannot +// benefit from this optimization yet anyhow. Besides, if you're importing a +// type here, you more than likely want to dump it and its fields. Loading all +// members populates lookup tables in the Clang Importer and ensures the +// absence of cache-fill-related side effects. +static void forceLoadAllMembers(IterableDeclContext *IDC) { + if (!IDC) return; + IDC->loadAllMembers(); +} + void ClangImporter::Implementation::lookupValueDWARF( DeclName name, NLKind lookupKind, Identifier inModule, SmallVectorImpl &results) { @@ -150,8 +161,10 @@ void ClangImporter::Implementation::lookupValueDWARF( continue; if (swiftDecl->getFullName().matchesRef(name) && - swiftDecl->getDeclContext()->isModuleScopeContext()) + swiftDecl->getDeclContext()->isModuleScopeContext()) { + forceLoadAllMembers(dyn_cast(swiftDecl)); results.push_back(swiftDecl); + } } } @@ -174,8 +187,10 @@ void ClangImporter::Implementation::lookupTypeDeclDWARF( Decl *importedDecl = cast_or_null( importDeclReal(namedDecl->getMostRecentDecl(), CurrentVersion)); - if (auto *importedType = dyn_cast_or_null(importedDecl)) + if (auto *importedType = dyn_cast_or_null(importedDecl)) { + forceLoadAllMembers(dyn_cast(importedType)); receiver(importedType); + } } } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index f734bb31d4d93..0d2b5f31555ef 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -104,7 +104,7 @@ getDefaultMakeStructRawValuedOptions() { return opts; } -static bool isInSystemModule(DeclContext *D) { +static bool isInSystemModule(const DeclContext *D) { return cast(D->getModuleScopeContext())->isSystemModule(); } @@ -2149,45 +2149,46 @@ namespace { } }; - /// Search the member tables for this class and its superclasses and try to identify the nearest VarDecl - /// that serves as a base for an override. We have to do this ourselves because Objective-C has no - /// semantic notion of overrides, and freely allows users to refine the type of any member property in a - /// derived class. + /// Search the member tables for this class and its superclasses and try to + /// identify the nearest VarDecl that serves as a base for an override. We + /// have to do this ourselves because Objective-C has no semantic notion of + /// overrides, and freely allows users to refine the type of any member + /// property in a derived class. /// - /// The override must be the nearest possible one so there are not breaks in the override chain. That is, - /// suppose C refines B refines A and each successively redeclares a member with a different type. It - /// should be the case that the nearest override from C is B and from B is A. If the override point from - /// C were A, then B would record an override on A as well and we would introduce a semantic ambiguity. + /// The override must be the nearest possible one so there are not breaks + /// in the override chain. That is, suppose C refines B refines A and each + /// successively redeclares a member with a different type. It should be + /// the case that the nearest override from C is B and from B is A. If the + /// override point from C were A, then B would record an override on A as + /// well and we would introduce a semantic ambiguity. /// - /// There is also a special case for finding a method that stomps over a getter. If this is the case and no - /// override point is identified, we will not import the property to force users to explicitly call the method. + /// There is also a special case for finding a method that stomps over a + /// getter. If this is the case and no override point is identified, we will + /// not import the property to force users to explicitly call the method. static std::pair - identifyNearestOverridenDecl(ClangImporter::Implementation &Impl, - DeclContext *dc, - const clang::ObjCPropertyDecl *decl, - Identifier name, - ClassDecl *subject) { + identifyNearestOverriddenDecl(ClangImporter::Implementation &Impl, + DeclContext *dc, + const clang::ObjCPropertyDecl *decl, + Identifier name, + ClassDecl *subject) { bool foundMethod = false; for (; subject; (subject = subject->getSuperclassDecl())) { llvm::SmallVector lookup; - auto found = Impl.MembersForNominal.find(subject); - if (found != Impl.MembersForNominal.end()) { - lookup.append(found->second.begin(), found->second.end()); - namelookup::pruneLookupResultSet(dc, NL_QualifiedDefault, lookup); + auto foundNames = Impl.MembersForNominal.find(subject); + if (foundNames != Impl.MembersForNominal.end()) { + auto foundDecls = foundNames->second.find(name); + if (foundDecls != foundNames->second.end()) { + lookup.append(foundDecls->second.begin(), foundDecls->second.end()); + } } for (auto *&result : lookup) { - // Skip declarations that don't match the name we're looking for. - if (result->getBaseName() != name) - continue; - if (auto *fd = dyn_cast(result)) { if (fd->isInstanceMember() != decl->isInstanceProperty()) continue; - if (fd->getFullName().getArgumentNames().empty()) { - foundMethod = true; - } + assert(fd->getFullName().getArgumentNames().empty()); + foundMethod = true; } else { auto *var = cast(result); if (var->isInstanceMember() != decl->isInstanceProperty()) @@ -2231,11 +2232,11 @@ namespace { return getVersion() == getActiveSwiftVersion(); } - template - T *recordMemberInContext(DeclContext *dc, T *member) { + void recordMemberInContext(DeclContext *dc, ValueDecl *member) { assert(member && "Attempted to record null member!"); - Impl.MembersForNominal[dc->getSelfNominalTypeDecl()].push_back(member); - return member; + auto *nominal = dc->getSelfNominalTypeDecl(); + auto name = member->getBaseName(); + Impl.MembersForNominal[nominal][name].push_back(member); } /// Import the name of the given entity. @@ -3798,7 +3799,7 @@ namespace { if (correctSwiftName) markAsVariant(result, *correctSwiftName); - return recordMemberInContext(dc, result); + return result; } void finishFuncDecl(const clang::FunctionDecl *decl, @@ -4091,7 +4092,7 @@ namespace { /// Check whether we have already imported a method with the given /// selector in the given context. bool isMethodAlreadyImported(ObjCSelector selector, bool isInstance, - DeclContext *dc, + const DeclContext *dc, llvm::function_ref filter) { // We only need to perform this check for classes. auto classDecl @@ -4408,7 +4409,14 @@ namespace { } } - return recordMemberInContext(dc, result); + // We only care about recording methods with no arguments here, because + // they can shadow imported properties. + if (!isa(result) && + result->getFullName().getArgumentNames().empty()) { + recordMemberInContext(dc, result); + } + + return result; } public: @@ -4425,7 +4433,7 @@ namespace { /// NSArray(capacity: 1024) /// \endcode ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, Optional kind, bool required); @@ -4454,7 +4462,7 @@ namespace { /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, CtorInitializerKind kind, bool required, @@ -4492,11 +4500,6 @@ namespace { const clang::ObjCProtocolList &clangProtocols, SmallVectorImpl &inheritedTypes); - /// Add conformances to the given Objective-C protocols to the - /// given declaration. - void addObjCProtocolConformances(Decl *decl, - ArrayRef protocols); - // Returns None on error. Returns nullptr if there is no type param list to // import or we suppress its import, as in the case of NSArray, NSSet, and // NSDictionary. @@ -4517,9 +4520,7 @@ namespace { /// methods become class methods on NSObject). void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, - SmallVectorImpl &members, - ASTContext &Ctx); + SmallVectorImpl &members); void importNonOverriddenMirroredMethods(DeclContext *dc, MutableArrayRef entries, @@ -4527,7 +4528,7 @@ namespace { /// Import constructors from our superclasses (and their /// categories/extensions), effectively "inheriting" constructors. - void importInheritedConstructors(ClassDecl *classDecl, + void importInheritedConstructors(const ClassDecl *classDecl, SmallVectorImpl &newMembers); Decl *VisitObjCCategoryDecl(const clang::ObjCCategoryDecl *decl) { @@ -5077,7 +5078,7 @@ namespace { bool foundMethod = false; std::tie(overridden, foundMethod) - = identifyNearestOverridenDecl(Impl, dc, decl, name, subject); + = identifyNearestOverriddenDecl(Impl, dc, decl, name, subject); if (foundMethod && !overridden) return nullptr; @@ -5172,7 +5173,8 @@ namespace { if (correctSwiftName) markAsVariant(result, *correctSwiftName); - return recordMemberInContext(dc, result); + recordMemberInContext(dc, result); + return result; } Decl * @@ -5458,7 +5460,7 @@ namespace { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto protoDecl = dyn_cast(found.second)) + if (auto protoDecl = dyn_cast(found.Item)) if (protoDecl == proto || protoDecl->inheritsFrom(proto)) return true; } @@ -6083,7 +6085,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName, } ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, Optional kind, bool required) { // Only methods in the 'init' family can become constructors. assert(isInitMethod(objcMethod) && "Not a real init method"); @@ -6234,7 +6236,7 @@ bool SwiftDeclConverter::existingConstructorIsWorse( /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, CtorInitializerKind kind, bool required, ObjCSelector selector, ImportedName importedName, ArrayRef args, bool variadic, bool &redundant) { @@ -6354,7 +6356,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), /*Throws=*/importedName.getErrorInfo().hasValue(), /*ThrowsLoc=*/SourceLoc(), bodyParams, - /*GenericParams=*/nullptr, dc); + /*GenericParams=*/nullptr, const_cast(dc)); addObjCAttribute(result, selector); @@ -6861,22 +6863,7 @@ void SwiftDeclConverter::importObjCProtocols( } } - addObjCProtocolConformances(decl, protocols); -} - -void SwiftDeclConverter::addObjCProtocolConformances( - Decl *decl, ArrayRef protocols) { - // Nothing to do for protocols. - if (isa(decl)) return; - Impl.recordImportedProtocols(decl, protocols); - - if (auto nominal = dyn_cast(decl)) { - nominal->setConformanceLoader(&Impl, 0); - } else { - auto ext = cast(decl); - ext->setConformanceLoader(&Impl, 0); - } } Optional SwiftDeclConverter::importObjCGenericParams( @@ -6941,23 +6928,18 @@ Optional SwiftDeclConverter::importObjCGenericParams( void SwiftDeclConverter::importMirroredProtocolMembers( const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, SmallVectorImpl &members, - ASTContext &Ctx) { + SmallVectorImpl &members) { assert(dc); const clang::ObjCInterfaceDecl *interfaceDecl = nullptr; const ClangModuleUnit *declModule; const ClangModuleUnit *interfaceModule; - // 'protocols' is, for some reason, the full recursive expansion of - // the protocol hierarchy, so there's no need to recursively descend - // into inherited protocols. - // Try to import only the most specific methods with a particular name. // We use a MapVector to get deterministic iteration order later. llvm::MapVector> methodsByName; - for (auto proto : protocols) { + for (auto proto : Impl.getImportedProtocols(dc->getAsDecl())) { auto clangProto = cast_or_null(proto->getClangDecl()); if (!clangProto) @@ -7189,7 +7171,7 @@ void SwiftDeclConverter::importNonOverriddenMirroredMethods(DeclContext *dc, } void SwiftDeclConverter::importInheritedConstructors( - ClassDecl *classDecl, SmallVectorImpl &newMembers) { + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { if (!classDecl->hasSuperclass()) return; @@ -7557,6 +7539,15 @@ void ClangImporter::Implementation::importAttributes( } } + if (auto method = dyn_cast(ClangDecl)) { + if (method->isDirectMethod() && !AnyUnavailable) { + auto attr = AvailableAttr::createPlatformAgnostic( + C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift); + MappedDecl->getAttrs().add(attr); + AnyUnavailable = true; + } + } + // If the declaration is unavailable, we're done. if (AnyUnavailable) return; @@ -7688,7 +7679,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl, if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) { if (auto swiftClass = castIgnoringCompatibilityAlias( importDecl(theClass, CurrentVersion))) { - swiftClass->setHasMissingDesignatedInitializers(); + SwiftContext.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{swiftClass}, true); } } } @@ -8536,7 +8528,7 @@ void ClangImporter::Implementation::loadAllMembersIntoExtension( startedImportingEntity(); // Load the members. - for (auto entry : table->lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table->allGlobalsAsMembersInContext(effectiveClangContext)) { auto decl = entry.get(); // Only include members in the same submodule as this extension. @@ -8592,20 +8584,6 @@ bool ClangImporter::Implementation::addMemberAndAlternatesToExtension( return true; } -static ExtensionDecl * -figureOutTheDeclarationContextToImportInto(Decl *D, DeclContext *&DC, - IterableDeclContext *&IDC) { - if (auto *nominal = dyn_cast(D)) { - DC = nominal; - IDC = nominal; - return nullptr; - } - ExtensionDecl *ext = cast(D); - DC = ext; - IDC = ext; - return ext; -} - static void loadMembersOfBaseImportedFromClang(ExtensionDecl *ext) { const NominalTypeDecl *base = ext->getExtendedNominal(); auto *clangBase = base->getClangDecl(); @@ -8627,19 +8605,18 @@ void ClangImporter::Implementation::loadAllMembersOfObjcContainer( Instance->getSourceManager(), "loading members for"); - DeclContext *DC; - IterableDeclContext *IDC; - if (ExtensionDecl *ext = - figureOutTheDeclarationContextToImportInto(D, DC, IDC)) { - // If the base is also imported from Clang, load its members first. + assert(isa(D) || isa(D)); + if (auto *ext = dyn_cast(D)) { + // If the extended type is also imported from Clang, load its members first. loadMembersOfBaseImportedFromClang(ext); } startedImportingEntity(); SmallVector members; - collectMembersToAdd(objcContainer, D, DC, members); + collectMembersToAdd(objcContainer, D, cast(D), members); + auto *IDC = cast(D); for (auto member : members) { if (!isa(member)) IDC->addMember(member); @@ -8673,6 +8650,15 @@ void ClangImporter::Implementation::insertMembersAndAlternates( }); } +void ClangImporter::Implementation::importInheritedConstructors( + const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { + if (curObjCClass->getName() != "Protocol") { + SwiftDeclConverter converter(*this, CurrentVersion); + converter.importInheritedConstructors(classDecl, newMembers); + } + } + void ClangImporter::Implementation::collectMembersToAdd( const clang::ObjCContainerDecl *objcContainer, Decl *D, DeclContext *DC, SmallVectorImpl &members) { @@ -8684,18 +8670,9 @@ void ClangImporter::Implementation::collectMembersToAdd( } SwiftDeclConverter converter(*this, CurrentVersion); - - auto protos = getImportedProtocols(D); if (auto clangClass = dyn_cast(objcContainer)) { - auto swiftClass = cast(D); objcContainer = clangClass = clangClass->getDefinition(); - - // Imported inherited initializers. - if (clangClass->getName() != "Protocol") { - converter.importInheritedConstructors(const_cast(swiftClass), - members); - } - + importInheritedConstructors(clangClass, cast(D), members); } else if (auto clangProto = dyn_cast(objcContainer)) { objcContainer = clangProto->getDefinition(); @@ -8703,8 +8680,7 @@ void ClangImporter::Implementation::collectMembersToAdd( // Import mirrored declarations for protocols to which this category // or extension conforms. // FIXME: This is supposed to be a short-term hack. - converter.importMirroredProtocolMembers(objcContainer, DC, - protos, members, SwiftContext); + converter.importMirroredProtocolMembers(objcContainer, DC, members); } void ClangImporter::Implementation::loadAllConformances( diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 8e112ab39efce..2deb1a8045edd 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -185,7 +185,7 @@ namespace { llvm_unreachable("Dependent types cannot be converted"); \ } #define TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Given a loaded type like CInt, look through the type alias sugar that the // stdlib uses to show the underlying type. We want to import the signature diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 79d0e175e211d..8f1bd96b4000f 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -447,10 +447,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation // Mapping from imported types to their raw value types. llvm::DenseMap RawTypes; - /// Keep track of all member declarations that have been imported into a nominal type. - llvm::DenseMap> - MembersForNominal; - clang::CompilerInstance *getClangInstance() { return Instance.get(); } @@ -492,7 +488,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Keep track of initializer declarations that correspond to /// imported methods. llvm::DenseMap< - std::tuple, + std::tuple, ConstructorDecl *> Constructors; /// Keep track of all initializers that have been imported into a @@ -500,6 +496,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm::DenseMap> ConstructorsForNominal; + /// Keep track of all member declarations that have been imported into + /// a nominal type. + llvm::DenseMap>> + MembersForNominal; + /// Keep track of the nested 'Code' enum for imported error wrapper /// structs. llvm::DenseMap ErrorCodeEnums; @@ -605,6 +608,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool shouldIgnoreBridgeHeaderTopLevelDecl(clang::Decl *D); private: + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport; + /// The DWARF importer delegate, if installed. DWARFImporterDelegate *DWARFImporter = nullptr; public: @@ -614,17 +621,21 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation private: /// The list of Clang modules found in the debug info. llvm::DenseMap DWARFModuleUnits; -public: + /// Load a module using the clang::CompilerInstance. ModuleDecl *loadModuleClang(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); /// "Load" a module from debug info. Because debug info types are read on /// demand, this doesn't really do any work. ModuleDecl *loadModuleDWARF(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); + +public: + /// Load a module using either method. + ModuleDecl *loadModule(SourceLoc importLoc, + ArrayRef> path); - void recordImplicitUnwrapForDecl(ValueDecl *decl, bool isIUO) { if (!isIUO) return; @@ -804,6 +815,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation Decl *importMirroredDecl(const clang::NamedDecl *decl, DeclContext *dc, Version version, ProtocolDecl *proto); + void importInheritedConstructors(const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, + SmallVectorImpl &newMembers); + /// Utility function for building simple generic signatures. GenericSignature buildGenericSignature(GenericParamList *genericParams, DeclContext *dc); @@ -1168,12 +1183,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// FIXME: This is all a hack; we should have lazier deserialization /// of protocols separate from their conformances. - void recordImportedProtocols(const Decl *decl, + void recordImportedProtocols(Decl *decl, ArrayRef protocols) { + // Nothing to do for protocols. + if (isa(decl)) return; + if (protocols.empty()) return; ImportedProtocols[decl] = SwiftContext.AllocateCopy(protocols); + + if (auto nominal = dyn_cast(decl)) { + nominal->setConformanceLoader(this, 0); + } else { + auto ext = cast(decl); + ext->setConformanceLoader(this, 0); + } } /// Retrieve the imported protocols for the given declaration. diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 632edc4527a0e..0aa7476df5d55 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -58,6 +58,9 @@ namespace { llvm::OnDiskIterableChainedHashTable; using SerializedGlobalsAsMembersTable = + llvm::OnDiskIterableChainedHashTable; + + using SerializedGlobalsAsMembersIndex = llvm::OnDiskIterableChainedHashTable; } // end anonymous namespace @@ -99,6 +102,7 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { std::unique_ptr SerializedTable; ArrayRef Categories; std::unique_ptr GlobalsAsMembersTable; + std::unique_ptr GlobalsAsMembersIndex; SwiftLookupTableReader(clang::ModuleFileExtension *extension, clang::ASTReader &reader, @@ -108,11 +112,14 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { serializedTable, ArrayRef categories, std::unique_ptr - globalsAsMembersTable) + globalsAsMembersTable, + std::unique_ptr + globalsAsMembersIndex) : ModuleFileExtensionReader(extension), Reader(reader), ModuleFile(moduleFile), OnRemove(onRemove), SerializedTable(std::move(serializedTable)), Categories(categories), - GlobalsAsMembersTable(std::move(globalsAsMembersTable)) {} + GlobalsAsMembersTable(std::move(globalsAsMembersTable)), + GlobalsAsMembersIndex(std::move(globalsAsMembersIndex)) {} public: /// Create a new lookup table reader for the given AST reader and stream @@ -148,12 +155,22 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { /// injected into them. SmallVector getGlobalsAsMembersContexts(); + SmallVector getGlobalsAsMembersBaseNames(); + /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. /// /// \returns true if we found anything, false otherwise. - bool lookupGlobalsAsMembers(SwiftLookupTable::StoredContext context, - SmallVectorImpl &entries); + bool lookupGlobalsAsMembersInContext(SwiftLookupTable::StoredContext context, + SmallVectorImpl &entries); + + /// Retrieve the set of global declarations that are going to be imported as members under the given + /// Swift base name. + /// + /// \returns true if we found anything, false otherwise. + bool lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries); }; } // namespace swift @@ -491,63 +508,81 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, return; } - // Populate cache from reader if necessary. - findOrCreate(name.getBaseName()); + auto updateTableWithEntry = [this](SingleEntry newEntry, StoredContext context, + TableType::value_type::second_type &entries){ + for (auto &entry : entries) { + if (entry.Context == context) { + // We have entries for this context. + (void)addLocalEntry(newEntry, entry.DeclsOrMacros); + return; + } else { + (void)newEntry; + } + } - auto context = *contextOpt; + // This is a new context for this name. Add it. + auto decl = newEntry.dyn_cast(); + auto macro = newEntry.dyn_cast(); + auto moduleMacro = newEntry.dyn_cast(); + + FullTableEntry entry; + entry.Context = context; + if (decl) + entry.DeclsOrMacros.push_back(encodeEntry(decl)); + else if (macro) + entry.DeclsOrMacros.push_back(encodeEntry(macro)); + else + entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); + + entries.push_back(entry); + }; // If this is a global imported as a member, record is as such. + auto context = *contextOpt; if (isGlobalAsMember(newEntry, context)) { - auto &entries = GlobalsAsMembers[context]; + // Populate cache from reader if necessary. + findOrCreate(GlobalsAsMembers, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + updateTableWithEntry(newEntry, context, + GlobalsAsMembers[name.getBaseName()]); + + // Populate the index as well. + auto &entries = GlobalsAsMembersIndex[context]; (void)addLocalEntry(newEntry, entries); } - // Find the list of entries for this base name. - auto &entries = LookupTable[name.getBaseName()]; - auto decl = newEntry.dyn_cast(); - auto macro = newEntry.dyn_cast(); - auto moduleMacro = newEntry.dyn_cast(); - for (auto &entry : entries) { - if (entry.Context == context) { - // We have entries for this context. - (void)addLocalEntry(newEntry, entry.DeclsOrMacros); - return; - } - } - - // This is a new context for this name. Add it. - FullTableEntry entry; - entry.Context = context; - if (decl) - entry.DeclsOrMacros.push_back(encodeEntry(decl)); - else if (macro) - entry.DeclsOrMacros.push_back(encodeEntry(macro)); - else - entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); - entries.push_back(entry); + // Populate cache from reader if necessary. + findOrCreate(LookupTable, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); + updateTableWithEntry(newEntry, context, LookupTable[name.getBaseName()]); } -auto SwiftLookupTable::findOrCreate(SerializedSwiftName baseName) - -> llvm::DenseMap>::iterator { +SwiftLookupTable::TableType::iterator +SwiftLookupTable::findOrCreate(TableType &Table, + SerializedSwiftName baseName, + llvm::function_ref create) { // If there is no base name, there is nothing to find. - if (baseName.empty()) return LookupTable.end(); + if (baseName.empty()) return Table.end(); // Find entries for this base name. - auto known = LookupTable.find(baseName); + auto known = Table.find(baseName); // If we found something, we're done. - if (known != LookupTable.end()) return known; + if (known != Table.end()) return known; // If there's no reader, we've found all there is to find. if (!Reader) return known; // Lookup this base name in the module file. SmallVector results; - (void)Reader->lookup(baseName, results); + create(results, *Reader, baseName); // Add an entry to the table so we don't look again. - known = LookupTable.insert({ std::move(baseName), std::move(results) }).first; + known = Table.insert({ std::move(baseName), std::move(results) }).first; return known; } @@ -558,7 +593,10 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -578,24 +616,53 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { +SwiftLookupTable::lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext) { SmallVector result; // Find entries for this base name. - auto known = GlobalsAsMembers.find(context); + auto known = findOrCreate(GlobalsAsMembers, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + if (known == GlobalsAsMembers.end()) return result; + + + // Walk each of the entries. + for (auto &entry : known->second) { + // If we're looking in a particular context and it doesn't match the + // entry context, we're done. + if (searchContext && entry.Context != *searchContext) + continue; + + // Map each of the declarations. + for (auto &stored : entry.DeclsOrMacros) + if (auto entry = mapStored(stored)) + result.push_back(entry); + } + + return result; +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(StoredContext context) { + SmallVector result; + + // Find entries for this base name. + auto known = GlobalsAsMembersIndex.find(context); // If we didn't find anything... - if (known == GlobalsAsMembers.end()) { + if (known == GlobalsAsMembersIndex.end()) { // If there's no reader, we've found all there is to find. if (!Reader) return result; // Lookup this base name in the module extension file. SmallVector results; - (void)Reader->lookupGlobalsAsMembers(context, results); + (void)Reader->lookupGlobalsAsMembersInContext(context, results); // Add an entry to the table so we don't look again. - known = GlobalsAsMembers.insert({ std::move(context), - std::move(results) }).first; + known = GlobalsAsMembersIndex.insert({ std::move(context), + std::move(results) }).first; } // Map each of the results. @@ -607,14 +674,26 @@ SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(EffectiveClangContext context) { - // Translate context. +SwiftLookupTable::lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext) { + // Propagate the null search context. + if (!searchContext) + return lookupGlobalsAsMembersImpl(baseName, None); + + Optional storedContext = translateContext(*searchContext); + if (!storedContext) return { }; + + return lookupGlobalsAsMembersImpl(baseName, *storedContext); +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(EffectiveClangContext context) { if (!context) return { }; Optional storedContext = translateContext(context); if (!storedContext) return { }; - return lookupGlobalsAsMembers(*storedContext); + return allGlobalsAsMembersInContext(*storedContext); } SmallVector @@ -622,13 +701,13 @@ SwiftLookupTable::allGlobalsAsMembers() { // If we have a reader, deserialize all of the globals-as-members data. if (Reader) { for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } // Collect all of the keys and sort them. SmallVector contexts; - for (const auto &globalAsMember : GlobalsAsMembers) { + for (const auto &globalAsMember : GlobalsAsMembersIndex) { contexts.push_back(globalAsMember.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -636,12 +715,25 @@ SwiftLookupTable::allGlobalsAsMembers() { // Collect all of the results in order. SmallVector results; for (const auto &context : contexts) { - for (auto &entry : GlobalsAsMembers[context]) + for (auto &entry : GlobalsAsMembersIndex[context]) results.push_back(mapStored(entry)); } return results; } +SmallVector +SwiftLookupTable::allGlobalsAsMembersBaseNames() { + // If we have a reader, enumerate its base names. + if (Reader) return Reader->getGlobalsAsMembersBaseNames(); + + // Otherwise, walk the lookup table. + SmallVector result; + for (const auto &entry : GlobalsAsMembers) { + result.push_back(entry.first); + } + return result; +} + SmallVector SwiftLookupTable::lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext) { @@ -672,7 +764,10 @@ SwiftLookupTable::lookupObjCMembers(SerializedSwiftName baseName) { SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -774,10 +869,14 @@ void SwiftLookupTable::deserializeAll() { (void)lookup(baseName, None); } + for (auto baseName : Reader->getGlobalsAsMembersBaseNames()) { + (void)lookupGlobalsAsMembersImpl(baseName, None); + } + (void)categories(); for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } @@ -908,10 +1007,10 @@ void SwiftLookupTable::dump(raw_ostream &os) const { os << "\n"; } - if (!GlobalsAsMembers.empty()) { + if (!GlobalsAsMembersIndex.empty()) { os << "Globals-as-members mapping:\n"; SmallVector contexts; - for (const auto &entry : GlobalsAsMembers) { + for (const auto &entry : GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -920,7 +1019,7 @@ void SwiftLookupTable::dump(raw_ostream &os) const { printStoredContext(context, os); os << ": "; - const auto &entries = GlobalsAsMembers.find(context)->second; + const auto &entries = GlobalsAsMembersIndex.find(context)->second; interleave(entries.begin(), entries.end(), [this, &os](uint64_t entry) { printStoredEntry(this, entry, os); @@ -955,7 +1054,11 @@ namespace { /// Record that contains the mapping from contexts to the list of /// globals that will be injected as members into those contexts. - GLOBALS_AS_MEMBERS_RECORD_ID + GLOBALS_AS_MEMBERS_RECORD_ID, + + /// Record that contains the mapping from contexts to the list of + /// globals that will be injected as members into those contexts. + GLOBALS_AS_MEMBERS_INDEX_RECORD_ID, }; using BaseNameToEntitiesTableRecordLayout @@ -967,6 +1070,9 @@ namespace { using GlobalsAsMembersTableRecordLayout = BCRecordLayout, BCBlob>; + using GlobalsAsMembersIndexRecordLayout + = BCRecordLayout, BCBlob>; + /// Trait used to write the on-disk hash table for the base name -> entities /// mapping. class BaseNameToEntitiesTableWriterInfo { @@ -1212,9 +1318,39 @@ void SwiftLookupTableWriter::writeExtensionContents( // Write the globals-as-members table, if non-empty. if (!table.GlobalsAsMembers.empty()) { + // First, gather the sorted list of base names. + SmallVector baseNames; + for (const auto &entry : table.GlobalsAsMembers) + baseNames.push_back(entry.first); + llvm::array_pod_sort(baseNames.begin(), baseNames.end()); + + // Form the mapping from base names to entities with their context. + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + generator; + BaseNameToEntitiesTableWriterInfo info(table, Writer); + for (auto baseName : baseNames) + generator.insert(baseName, table.GlobalsAsMembers[baseName], info); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream, info); + } + + GlobalsAsMembersTableRecordLayout layout(stream); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + } + + // Write the globals-as-members index, if non-empty. + if (!table.GlobalsAsMembersIndex.empty()) { // Sort the keys. SmallVector contexts; - for (const auto &entry : table.GlobalsAsMembers) { + for (const auto &entry : table.GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -1227,7 +1363,7 @@ void SwiftLookupTableWriter::writeExtensionContents( generator; GlobalsAsMembersTableWriterInfo info(table, Writer); for (auto context : contexts) - generator.insert(context, table.GlobalsAsMembers[context], info); + generator.insert(context, table.GlobalsAsMembersIndex[context], info); llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 @@ -1235,7 +1371,7 @@ void SwiftLookupTableWriter::writeExtensionContents( tableOffset = generator.Emit(blobStream, info); } - GlobalsAsMembersTableRecordLayout layout(stream); + GlobalsAsMembersIndexRecordLayout layout(stream); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } } @@ -1490,6 +1626,7 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, } llvm::BitstreamEntry next = maybeNext.get(); std::unique_ptr serializedTable; + std::unique_ptr globalsAsMembersIndex; std::unique_ptr globalsAsMembersTable; ArrayRef categories; while (next.Kind != llvm::BitstreamEntry::EndBlock) { @@ -1539,6 +1676,22 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, break; } + case GLOBALS_AS_MEMBERS_INDEX_RECORD_ID: { + // Already saw globals as members index. + if (globalsAsMembersIndex) + return nullptr; + + uint32_t tableOffset; + GlobalsAsMembersIndexRecordLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + globalsAsMembersIndex.reset( + SerializedGlobalsAsMembersIndex::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + case CATEGORIES_RECORD_ID: { // Already saw categories; input is malformed. if (!categories.empty()) return nullptr; @@ -1590,7 +1743,8 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, return std::unique_ptr( new SwiftLookupTableReader(extension, reader, moduleFile, onRemove, std::move(serializedTable), categories, - std::move(globalsAsMembersTable))); + std::move(globalsAsMembersTable), + std::move(globalsAsMembersIndex))); } @@ -1617,21 +1771,46 @@ bool SwiftLookupTableReader::lookup( SmallVector SwiftLookupTableReader::getGlobalsAsMembersContexts() { SmallVector results; - if (!GlobalsAsMembersTable) return results; + if (!GlobalsAsMembersIndex) return results; - for (auto key : GlobalsAsMembersTable->keys()) { + for (auto key : GlobalsAsMembersIndex->keys()) { results.push_back(key); } return results; } -bool SwiftLookupTableReader::lookupGlobalsAsMembers( +bool SwiftLookupTableReader::lookupGlobalsAsMembersInContext( SwiftLookupTable::StoredContext context, SmallVectorImpl &entries) { + if (!GlobalsAsMembersIndex) return false; + + // Look for an entry with this context name. + auto known = GlobalsAsMembersIndex->find(context); + if (known == GlobalsAsMembersIndex->end()) return false; + + // Grab the results. + entries = std::move(*known); + return true; +} + +SmallVector +SwiftLookupTableReader::getGlobalsAsMembersBaseNames() { + SmallVector results; + if (!GlobalsAsMembersTable) return {}; + + for (auto key : GlobalsAsMembersTable->keys()) { + results.push_back(key); + } + return results; +} + +bool SwiftLookupTableReader::lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries) { if (!GlobalsAsMembersTable) return false; // Look for an entry with this context name. - auto known = GlobalsAsMembersTable->find(context); + auto known = GlobalsAsMembersTable->find(baseName); if (known == GlobalsAsMembersTable->end()) return false; // Grab the results. diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 3b8fc8c6a0b2d..fe980625c0ff8 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -276,7 +276,7 @@ const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MAJOR = 1; /// Lookup table minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 15; // Special names +const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 16; // Lazy Member Loading Index /// A lookup table that maps Swift names to the set of Clang /// declarations with that particular name. @@ -385,10 +385,19 @@ class SwiftLookupTable { } private: + using TableType = + llvm::DenseMap>; + using CacheCallback = void(SmallVectorImpl &, + SwiftLookupTableReader &, + SerializedSwiftName); + /// A table mapping from the base name of Swift entities to all of /// the C entities that have that name, in all contexts. - llvm::DenseMap> - LookupTable; + TableType LookupTable; + + /// A table mapping the base names of Swift entities to all of the C entities + /// that are remapped to that name by the globals-as-members utility, in all contexts. + TableType GlobalsAsMembers; /// The list of Objective-C categories and extensions. llvm::SmallVector Categories; @@ -398,7 +407,7 @@ class SwiftLookupTable { /// /// The values use the same representation as /// FullTableEntry::DeclsOrMacros. - llvm::DenseMap> GlobalsAsMembers; + llvm::DenseMap> GlobalsAsMembersIndex; /// The reader responsible for lazily loading the contents of this table. SwiftLookupTableReader *Reader; @@ -413,7 +422,9 @@ class SwiftLookupTable { /// Find or create the table entry for the given base name. llvm::DenseMap>::iterator - findOrCreate(SerializedSwiftName baseName); + findOrCreate(TableType &table, + SerializedSwiftName baseName, + llvm::function_ref create); /// Add the given entry to the list of entries, if it's not already /// present. @@ -474,8 +485,22 @@ class SwiftLookupTable { llvm::Optional searchContext); /// Retrieve the set of global declarations that are going to be - /// imported as members into the given context. - SmallVector lookupGlobalsAsMembers(StoredContext context); + /// imported as the given Swift name into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. This may be None to indicate that + /// all results from all contexts should be produced. + SmallVector + lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext); + + /// Retrieve the set of global declarations that are going to be imported as + /// members in the given context. + SmallVector + allGlobalsAsMembersInContext(StoredContext context); public: /// Lookup an unresolved context name and resolve it to a Clang @@ -488,14 +513,16 @@ class SwiftLookupTable { /// have this base name. /// /// \param searchContext The context in which the resulting set of - /// entities should reside. This may be None to indicate that - /// all results from all contexts should be produced. + /// entities should reside. SmallVector lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext); /// Retrieve the set of base names that are stored in the lookup table. SmallVector allBaseNames(); + /// Retrieve the set of base names that are stored in the globals-as-members lookup table. + SmallVector allGlobalsAsMembersBaseNames(); + /// Lookup Objective-C members with the given base name, regardless /// of context. SmallVector @@ -506,13 +533,28 @@ class SwiftLookupTable { /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. SmallVector - lookupGlobalsAsMembers(EffectiveClangContext context); + lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext); + + SmallVector + allGlobalsAsMembersInContext(EffectiveClangContext context); /// Retrieve the set of global declarations that are going to be /// imported as members. SmallVector allGlobalsAsMembers(); + /// Retrieve the set of base names that are going to be imported as members in the + /// given context. + SmallVector + globalsAsMembersBaseNames(EffectiveClangContext context); + /// Deserialize all entries. void deserializeAll(); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index b868b5b61e83e..90a2304df0064 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -529,7 +529,7 @@ NodePointer Demangler::demangleSymbol(StringRef MangledName, // Demangle old-style class and protocol names, which are still used in the // ObjC metadata. if (nextIf("_Tt")) - return demangleObjCTypeName(); + return demangleOldSymbolAsNode(Text, *this); unsigned PrefixLength = getManglingPrefixLength(MangledName); if (PrefixLength == 0) @@ -3212,45 +3212,3 @@ NodePointer Demangler::demangleValueWitness() { addChild(VW, createNode(Node::Kind::Index, unsigned(Kind))); return addChild(VW, popNode(Node::Kind::Type)); } - -NodePointer Demangler::demangleObjCTypeName() { - NodePointer Ty = createNode(Node::Kind::Type); - NodePointer Global = addChild(createNode(Node::Kind::Global), - addChild(createNode(Node::Kind::TypeMangling), Ty)); - NodePointer Nominal = nullptr; - bool isProto = false; - if (nextIf('C')) { - Nominal = createNode(Node::Kind::Class); - addChild(Ty, Nominal); - } else if (nextIf('P')) { - isProto = true; - Nominal = createNode(Node::Kind::Protocol); - addChild(Ty, addChild(createNode(Node::Kind::ProtocolList), - addChild(createNode(Node::Kind::TypeList), - addChild(createNode(Node::Kind::Type), Nominal)))); - } else { - return nullptr; - } - - if (nextIf('s')) { - Nominal->addChild(createNode(Node::Kind::Module, "Swift"), *this); - } else { - NodePointer Module = demangleIdentifier(); - if (!Module) - return nullptr; - Nominal->addChild(changeKind(Module, Node::Kind::Module), *this); - } - - NodePointer Ident = demangleIdentifier(); - if (!Ident) - return nullptr; - Nominal->addChild(Ident, *this); - - if (isProto && !nextIf('_')) - return nullptr; - - if (Pos < Text.size()) - return nullptr; - - return Global; -} diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 448f5044b7064..d1c4893e30152 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -33,8 +33,8 @@ target_link_libraries(swiftDriver PRIVATE if(SWIFT_BUILD_STATIC_STDLIB) set(static_stdlib_lnk_file_list) foreach(sdk ${SWIFT_CONFIGURED_SDKS}) + string(TOLOWER "${sdk}" lowercase_sdk) if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") - string(TOLOWER "${sdk}" lowercase_sdk) if(SWIFT_${SWIFT_HOST_VARIANT_SDK}_${SWIFT_HOST_VARIANT_ARCH}_ICU_STATICLIB) set(ICU_STATICLIB "TRUE") else() @@ -61,6 +61,44 @@ if(SWIFT_BUILD_STATIC_STDLIB) swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) + elseif("${sdk}" STREQUAL "WASI") + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/webassembly/static-stdlib-args.lnk") + set(linkfile "${lowercase_sdk}/static-stdlib-args.lnk") + add_custom_command_target(swift_static_stdlib_${sdk}_args + COMMAND + "${CMAKE_COMMAND}" -E copy + "${linkfile_src}" + "${SWIFTSTATICLIB_DIR}/${linkfile}" + OUTPUT + "${SWIFTSTATICLIB_DIR}/${linkfile}" + DEPENDS + "${linkfile_src}") + + list(APPEND static_stdlib_lnk_file_list ${swift_static_stdlib_${sdk}_args}) + swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" + DESTINATION "lib/swift_static/${lowercase_sdk}" + COMPONENT stdlib) + set(swift_icu_libs_wasi_list) + set(icu_modules UC I18N DATA) + foreach(module IN LISTS icu_modules) + set(module_lib "${SWIFT_WASI_wasm32_ICU_${module}}") + get_filename_component(module_lib_name ${module_lib} NAME) + add_custom_command_target(swift_icu_${module}_${sdk} + COMMAND + "${CMAKE_COMMAND}" -E copy + "${module_lib}" + "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + OUTPUT + "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + DEPENDS + "${module_lib}") + list(APPEND swift_icu_libs_wasi_list ${swift_icu_${module}_${sdk}}) + swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${module_lib_name}" + DESTINATION "lib/swift_static/${lowercase_sdk}" + COMPONENT stdlib) + endforeach() + add_custom_target(swift_icu_libs_wasi ALL DEPENDS ${swift_icu_libs_wasi_list}) + add_dependencies(stdlib swift_icu_libs_wasi) endif() endforeach() add_custom_target(swift_static_lnk_args ALL DEPENDS ${static_stdlib_lnk_file_list}) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 6cf8a34e6a378..b830e04ea68d4 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -48,6 +48,7 @@ #include "CompilationRecord.h" +#include #include #define DEBUG_TYPE "batch-mode" @@ -121,6 +122,7 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool SaveTemps, bool ShowDriverTimeCompilation, std::unique_ptr StatsReporter, + bool OnlyOneDependencyFile, bool EnableFineGrainedDependencies, bool VerifyFineGrainedDependencyGraphAfterEveryImport, bool EmitFineGrainedDependencyDotFileAfterEveryImport, @@ -149,6 +151,7 @@ Compilation::Compilation(DiagnosticEngine &Diags, ShowDriverTimeCompilation(ShowDriverTimeCompilation), Stats(std::move(StatsReporter)), FilelistThreshold(FilelistThreshold), + OnlyOneDependencyFile(OnlyOneDependencyFile), EnableFineGrainedDependencies(EnableFineGrainedDependencies), VerifyFineGrainedDependencyGraphAfterEveryImport( VerifyFineGrainedDependencyGraphAfterEveryImport), @@ -156,7 +159,8 @@ Compilation::Compilation(DiagnosticEngine &Diags, EmitFineGrainedDependencyDotFileAfterEveryImport), FineGrainedDependenciesIncludeIntrafileOnes( FineGrainedDependenciesIncludeIntrafileOnes), - EnableSourceRangeDependencies(EnableSourceRangeDependencies) { + EnableSourceRangeDependencies(EnableSourceRangeDependencies) + { if (CompareIncrementalSchemes) IncrementalComparator.emplace( // Ensure the references are to inst vars, NOT arguments @@ -240,7 +244,7 @@ namespace driver { /// Dependency graphs for deciding which jobs are dirty (need running) /// or clean (can be skipped). using CoarseGrainedDependencyGraph = - CoarseGrainedDependencyGraph; + swift::CoarseGrainedDependencyGraph; CoarseGrainedDependencyGraph CoarseGrainedDepGraph; CoarseGrainedDependencyGraph CoarseGrainedDepGraphForRanges; @@ -1357,11 +1361,20 @@ namespace driver { // subprocesses than before. And significantly: it's doing so while // not exceeding the RAM of a typical 2-core laptop. + // An explanation of why the partition calculation isn't integer division. + // Using an example, a module of 26 files exceeds the limit of 25 and must + // be compiled in 2 batches. Integer division yields 26/25 = 1 batch, but + // a single batch of 26 exceeds the limit. The calculation must round up, + // which can be calculated using: `(x + y - 1) / y` + auto DivideRoundingUp = [](size_t Num, size_t Div) -> size_t { + return (Num + Div - 1) / Div; + }; + size_t DefaultSizeLimit = 25; size_t NumTasks = TQ->getNumberOfParallelTasks(); size_t NumFiles = PendingExecution.size(); size_t SizeLimit = Comp.getBatchSizeLimit().getValueOr(DefaultSizeLimit); - return std::max(NumTasks, NumFiles / SizeLimit); + return std::max(NumTasks, DivideRoundingUp(NumFiles, SizeLimit)); } /// Select jobs that are batch-combinable from \c PendingExecution, combine @@ -2032,3 +2045,20 @@ unsigned Compilation::countSwiftInputs() const { ++inputCount; return inputCount; } + +void Compilation::addDependencyPathOrCreateDummy( + StringRef depPath, function_ref addDependencyPath) { + + if (!OnlyOneDependencyFile) { + addDependencyPath(); + return; + } + if (!HaveAlreadyAddedDependencyPath) { + addDependencyPath(); + HaveAlreadyAddedDependencyPath = true; + } else if (!depPath.empty()) { + // Create dummy empty file + std::error_code EC; + llvm::raw_fd_ostream(depPath, EC, llvm::sys::fs::F_None); + } +} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ba58e599b415e..34db9ebb425d8 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -98,6 +98,7 @@ void Driver::parseDriverKind(ArrayRef Args) { .Case("swiftc", DriverKind::Batch) .Case("swift-autolink-extract", DriverKind::AutolinkExtract) .Case("swift-indent", DriverKind::SwiftIndent) + .Case("swift-symbolgraph-extract", DriverKind::SymbolGraph) .Default(None); if (Kind.hasValue()) @@ -951,6 +952,11 @@ Driver::buildCompilation(const ToolChain &TC, ArgList->hasArg(options::OPT_driver_time_compilation); std::unique_ptr StatsReporter = createStatsReporter(ArgList.get(), Inputs, OI, DefaultTargetTriple); + + const bool OnlyOneDependencyFile = + ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, + options::OPT_disable_only_one_dependency_file, true); + // relies on the new dependency graph const bool EnableFineGrainedDependencies = ArgList->hasArg(options::OPT_enable_fine_grained_dependencies); @@ -984,6 +990,7 @@ Driver::buildCompilation(const ToolChain &TC, SaveTemps, ShowDriverTimeCompilation, std::move(StatsReporter), + OnlyOneDependencyFile, EnableFineGrainedDependencies, VerifyFineGrainedDependencyGraphAfterEveryImport, EmitFineGrainedDependencyDotFileAfterEveryImport, @@ -1758,6 +1765,17 @@ Driver::computeCompilerMode(const DerivedArgList &Args, const Arg *ArgRequiringSinglePrimaryCompile = Args.getLastArg(options::OPT_enable_source_range_dependencies); + // AST dump doesn't work with `-wmo`. Since it's not common to want to dump + // the AST, we assume that's the priority and ignore `-wmo`, but we warn the + // user about this decision. + // FIXME: AST dump also doesn't work with `-index-file`, but that fix is a bit + // more complicated than this. + if (Args.hasArg(options::OPT_whole_module_optimization) && + Args.hasArg(options::OPT_dump_ast)) { + Diags.diagnose(SourceLoc(), diag::warn_ignoring_wmo); + return OutputInfo::Mode::StandardCompile; + } + // Override batch mode if given -wmo or -index-file. if (ArgRequiringSingleCompile) { if (BatchModeOut) { @@ -2425,60 +2443,74 @@ static bool hasExistingAdditionalOutput(CommandOutput &output, return false; } -static void addAuxiliaryOutput( +static llvm::SmallString<128> computeAuxiliaryOutputPath( Compilation &C, CommandOutput &output, file_types::ID outputType, const TypeToPathMap *outputMap, StringRef workingDirectory, StringRef outputPath = StringRef(), llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { if (hasExistingAdditionalOutput(output, outputType, outputPath)) - return; + return {}; - StringRef outputMapPath; if (outputMap) { auto iter = outputMap->find(outputType); - if (iter != outputMap->end()) - outputMapPath = iter->second; + if (iter != outputMap->end()) { + StringRef outputMapPath = iter->second; + // Prefer a path from the OutputMap. + if (!outputMapPath.empty()) + return outputMapPath; + } } + if (!outputPath.empty()) + return outputPath; - if (!outputMapPath.empty()) { - // Prefer a path from the OutputMap. - output.setAdditionalOutputForType(outputType, outputMapPath); - } else if (!outputPath.empty()) { - output.setAdditionalOutputForType(outputType, outputPath); - } else if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { + if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { // This auxiliary output only exists if requireArg is passed, but it // wasn't this time. - return; - } else { - // Put the auxiliary output file next to "the" primary output file. - // - // FIXME: when we're in WMO and have multiple primary outputs, we derive the - // additional filename here from the _first_ primary output name, which - // means that in the derived OFM (in Job.cpp) the additional output will - // have a possibly-surprising name. But that's only half the problem: it - // also get associated with the first primary _input_, even when there are - // multiple primary inputs; really it should be associated with the build as - // a whole -- derived OFM input "" -- but that's a more general thing to - // fix. - llvm::SmallString<128> path; - if (output.getPrimaryOutputType() != file_types::TY_Nothing) - path = output.getPrimaryOutputFilenames()[0]; - else if (!output.getBaseInput(0).empty()) - path = llvm::sys::path::filename(output.getBaseInput(0)); - else { - formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", - workingDirectory, path); - } - assert(!path.empty()); - - bool isTempFile = C.isTemporaryFile(path); - llvm::sys::path::replace_extension( - path, file_types::getExtension(outputType)); - output.setAdditionalOutputForType(outputType, path); - if (isTempFile) - C.addTemporaryFile(path); + return {}; } + + // Put the auxiliary output file next to "the" primary output file. + // + // FIXME: when we're in WMO and have multiple primary outputs, we derive the + // additional filename here from the _first_ primary output name, which + // means that in the derived OFM (in Job.cpp) the additional output will + // have a possibly-surprising name. But that's only half the problem: it + // also get associated with the first primary _input_, even when there are + // multiple primary inputs; really it should be associated with the build as + // a whole -- derived OFM input "" -- but that's a more general thing to + // fix. + llvm::SmallString<128> path; + if (output.getPrimaryOutputType() != file_types::TY_Nothing) + path = output.getPrimaryOutputFilenames()[0]; + else if (!output.getBaseInput(0).empty()) + path = llvm::sys::path::filename(output.getBaseInput(0)); + else { + formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", + workingDirectory, path); + } + assert(!path.empty()); + + const bool isTempFile = C.isTemporaryFile(path); + llvm::sys::path::replace_extension(path, + file_types::getExtension(outputType)); + if (isTempFile) + C.addTemporaryFile(path); + return path; +} + +static void addAuxiliaryOutput( + Compilation &C, CommandOutput &output, file_types::ID outputType, + const TypeToPathMap *outputMap, StringRef workingDirectory, + StringRef outputPath = StringRef(), + llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { + + const auto path = + computeAuxiliaryOutputPath(C, output, outputType, outputMap, + workingDirectory, outputPath, requireArg); + if (path.empty()) + return; + output.setAdditionalOutputForType(outputType, path); } static void addDiagFileOutputForPersistentPCHAction( @@ -3057,8 +3089,12 @@ void Driver::chooseDependenciesOutputPaths(Compilation &C, llvm::SmallString<128> &Buf, CommandOutput *Output) const { if (C.getArgs().hasArg(options::OPT_emit_dependencies)) { - addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, - workingDirectory); + auto depPath = computeAuxiliaryOutputPath( + C, *Output, file_types::TY_Dependencies, OutputMap, workingDirectory); + C.addDependencyPathOrCreateDummy(depPath, [&] { + addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, + workingDirectory); + }); } if (C.getIncrementalBuildEnabled()) { file_types::forEachIncrementalOutputType([&](file_types::ID type) { @@ -3217,6 +3253,7 @@ void Driver::printHelp(bool ShowHidden) const { case DriverKind::Batch: case DriverKind::AutolinkExtract: case DriverKind::SwiftIndent: + case DriverKind::SymbolGraph: ExcludedFlagsBitmask |= options::NoBatchOption; break; } diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index 5627146dd7b46..a4e21ad0c9b6b 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -236,6 +236,10 @@ CommandOutput::getAdditionalOutputsForType(file_types::ID Type) const { return V; } +bool CommandOutput::hasAdditionalOutputForType(file_types::ID type) const { + return AdditionalOutputTypes.count(type); +} + StringRef CommandOutput::getAnyOutputForType(file_types::ID Type) const { if (PrimaryOutputType == Type) return getPrimaryOutputFilename(); diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index bf42c9dd02c7e..c8b8ab791692b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -238,6 +238,11 @@ bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { auto const *CJActA = dyn_cast(&A->getSource()); if (!CJActA) return false; + // When having only one job output a dependency file, that job is not + // batchable since it has an oddball set of additional output types. + if (C.OnlyOneDependencyFile && + A->getOutput().hasAdditionalOutputForType(file_types::TY_Dependencies)) + return false; return findSingleSwiftInput(CJActA) != nullptr; } diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 07a19d9e548ac..7607c31b86ff2 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -346,7 +346,12 @@ toolchains::GenericUnix::constructInvocation(const StaticLinkJobAction &job, ArgStringList Arguments; // Configure the toolchain. - const char *AR = "ar"; + const char *AR; + if (getTriple().isOSBinFormatWasm()) { + AR = "llvm-ar"; + } else { + AR = "ar"; + } Arguments.push_back("crs"); Arguments.push_back( diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 14296f5ad7227..fac378995366f 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -302,11 +302,12 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_interface_path); auto moduleSourceInfoOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_source_info_path); - + auto ldAddCFileOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_ldadd_cfile_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || - !moduleInterfaceOutput || !moduleSourceInfoOutput) { + !moduleInterfaceOutput || !moduleSourceInfoOutput || !ldAddCFileOutput) { return None; } std::vector result; @@ -328,6 +329,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.TBDPath = (*TBD)[i]; sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i]; sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; + sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; result.push_back(sop); } return result; @@ -446,6 +448,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.TBDPath = tbdPath; sop.ModuleInterfaceOutputPath = ModuleInterfaceOutputPath; sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; + sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; return sop; } @@ -546,7 +549,8 @@ SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { options::OPT_emit_loaded_module_trace_path, options::OPT_emit_module_interface_path, options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + options::OPT_emit_tbd_path, + options::OPT_emit_ldadd_cfile_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 7c3188f0dc168..0771ce1479aca 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -227,6 +227,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Opts.PreserveTypesAsWritten |= Args.hasArg(OPT_module_interface_preserve_types_as_written); + Opts.PrintFullConvention |= + Args.hasArg(OPT_experimental_print_full_convention); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running @@ -390,6 +392,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Args.getLastArg(OPT_debug_cycles)) Opts.DebugDumpCycles = true; + if (Args.getLastArg(OPT_build_request_dependency_graph)) + Opts.BuildRequestDependencyGraph = true; + if (const Arg *A = Args.getLastArg(OPT_output_request_graphviz)) { Opts.RequestEvaluatorGraphVizPath = A->getValue(); } @@ -638,7 +643,12 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, if (Args.hasArg(OPT_warnings_as_errors)) Opts.ExtraArgs.push_back("-Werror"); + Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support); + + Opts.DisableSourceImport |= + Args.hasArg(OPT_disable_clangimporter_source_import); + return false; } @@ -1235,6 +1245,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.DisableLegacyTypeInfo = true; } + if (Args.hasArg(OPT_prespecialize_generic_metadata)) { + Opts.PrespecializeGenericMetadata = true; + } + if (const Arg *A = Args.getLastArg(OPT_read_legacy_type_info_path_EQ)) { Opts.ReadLegacyTypeInfoPath = A->getValue(); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 2bfba22289d7c..a54f2852b7deb 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -124,6 +124,14 @@ std::string CompilerInvocation::getTBDPathForWholeModule() const { .SupplementaryOutputs.TBDPath; } +std::string +CompilerInvocation::getLdAddCFileOutputPathForWholeModule() const { + assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && + "LdAdd cfile only makes sense when the whole module can be seen"); + return getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.LdAddCFilePath; +} + std::string CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const { assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && @@ -770,7 +778,7 @@ ModuleDecl *CompilerInstance::importUnderlyingModule() { ModuleDecl *objCModuleUnderlyingMixedFramework = static_cast(Context->getClangModuleLoader()) ->loadModule(SourceLoc(), - std::make_pair(MainModule->getName(), SourceLoc())); + { Located(MainModule->getName(), SourceLoc()) }); if (objCModuleUnderlyingMixedFramework) return objCModuleUnderlyingMixedFramework; Diagnostics.diagnose(SourceLoc(), diag::error_underlying_module_not_found, @@ -800,7 +808,7 @@ void CompilerInstance::getImplicitlyImportedModules( if (Lexer::isIdentifier(ImplicitImportModuleName)) { auto moduleID = Context->getIdentifier(ImplicitImportModuleName); ModuleDecl *importModule = - Context->getModule(std::make_pair(moduleID, SourceLoc())); + Context->getModule({ Located(moduleID, SourceLoc()) }); if (importModule) { importModules.push_back(importModule); } else { @@ -886,12 +894,6 @@ void CompilerInstance::parseAndCheckTypesUpTo( } }); - if (Invocation.isCodeCompletion()) { - assert(limitStage == SourceFile::NameBound); - performCodeCompletionSecondPass(*PersistentState.get(), - *Invocation.getCodeCompletionFactory()); - } - // If the limiting AST stage is name binding, we're done. if (limitStage <= SourceFile::NameBound) { return; @@ -982,13 +984,13 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( // For SIL we actually have to interleave parsing and type checking // because the SIL parser expects to see fully type checked declarations. if (TheSILModule) { - if (Done || CurTUElem < MainFile.Decls.size()) { + if (Done || CurTUElem < MainFile.getTopLevelDecls().size()) { assert(mainIsPrimary); performTypeChecking(MainFile, CurTUElem); } } - CurTUElem = MainFile.Decls.size(); + CurTUElem = MainFile.getTopLevelDecls().size(); } while (!Done); if (!TheSILModule) { @@ -1069,7 +1071,7 @@ SourceFile *CompilerInstance::createSourceFileForMainModule( } void CompilerInstance::performParseOnly(bool EvaluateConditionals, - bool ParseDelayedBodyOnEnd) { + bool CanDelayBodies) { const InputFileKind Kind = Invocation.getInputKind(); ModuleDecl *const MainModule = getMainModule(); Context->LoadedModules[MainModule->getName()] = MainModule; @@ -1091,25 +1093,27 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, } PersistentState = llvm::make_unique(); + PersistentState->PerformConditionEvaluation = EvaluateConditionals; + + auto shouldDelayBodies = [&](unsigned bufferID) -> bool { + if (!CanDelayBodies) + return false; - SWIFT_DEFER { - if (ParseDelayedBodyOnEnd) - PersistentState->parseAllDelayedDeclLists(); + // Don't delay bodies in whole module mode or for primary files. + return !(isWholeModuleCompilation() || isPrimaryInput(bufferID)); }; - PersistentState->PerformConditionEvaluation = EvaluateConditionals; + // Parse all the library files. for (auto BufferID : InputSourceCodeBufferIDs) { if (BufferID == MainBufferID) continue; - auto IsPrimary = isWholeModuleCompilation() || isPrimaryInput(BufferID); - SourceFile *NextInput = createSourceFileForMainModule( SourceFileKind::Library, SourceFile::ImplicitModuleImportKind::None, BufferID); parseIntoSourceFileFull(*NextInput, BufferID, PersistentState.get(), - /*DelayBodyParsing=*/!IsPrimary); + shouldDelayBodies(BufferID)); } // Now parse the main file. @@ -1117,10 +1121,10 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, SourceFile &MainFile = MainModule->getMainSourceFile(Invocation.getSourceFileKind()); MainFile.SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache(); + assert(MainBufferID == MainFile.getBufferID()); - parseIntoSourceFileFull(MainFile, MainFile.getBufferID().getValue(), - PersistentState.get(), - /*DelayBodyParsing=*/false); + parseIntoSourceFileFull(MainFile, MainBufferID, PersistentState.get(), + shouldDelayBodies(MainBufferID)); } assert(Context->LoadedModules.size() == 1 && diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index f1bfcea821bc6..2f7a40f255c21 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/LockFileManager.h" using namespace swift; using FileDependency = SerializationOptions::FileDependency; @@ -240,7 +241,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( return false; } -bool ModuleInterfaceBuilder::buildSwiftModule( +bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer) { bool SubError = false; @@ -348,6 +349,8 @@ bool ModuleInterfaceBuilder::buildSwiftModule( std::string OutPathStr = OutPath; SerializationOpts.OutputPath = OutPathStr.c_str(); SerializationOpts.ModuleLinkName = FEOpts.ModuleLinkName; + SerializationOpts.AutolinkForceLoad = + !subInvocation.getIRGenOptions().ForceLoadSymbolName.empty(); // Record any non-SDK module interface files for the debug info. StringRef SDKPath = SubInstance.getASTContext().SearchPathOpts.SDKPath; @@ -382,3 +385,71 @@ bool ModuleInterfaceBuilder::buildSwiftModule( }); return !RunSuccess || SubError; } + +bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, + bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild) { + + while (1) { + // Attempt to lock the interface file. Only one process is allowed to build + // module from the interface so we don't consume too much memory when multiple + // processes are doing the same. + // FIXME: We should surface the module building step to the build system so + // we don't need to synchronize here. + llvm::LockFileManager Locked(interfacePath); + switch (Locked) { + case llvm::LockFileManager::LFS_Error:{ + // ModuleInterfaceBuilder takes care of correctness and locks are only + // necessary for performance. Fallback to building the module in case of any lock + // related errors. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_failure, + interfacePath); + } + // Clear out any potential leftover. + Locked.unsafeRemoveLockFile(); + LLVM_FALLTHROUGH; + } + case llvm::LockFileManager::LFS_Owned: { + if (RemarkRebuild) { + RemarkRebuild(); + } + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + } + case llvm::LockFileManager::LFS_Shared: { + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: { + // This process may have a different module output path. If the other + // process doesn't build the interface to this output path, we should try + // building ourselves. + auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath); + if (!bufferOrError) + continue; + if (ModuleBuffer) + *ModuleBuffer = std::move(bufferOrError.get()); + return false; + } + case llvm::LockFileManager::Res_OwnerDied: { + continue; // try again to get the lock. + } + case llvm::LockFileManager::Res_Timeout: { + // Since ModuleInterfaceBuilder takes care of correctness, we try waiting for + // another process to complete the build so swift does not do it done + // twice. If case of timeout, build it ourselves. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out, + interfacePath); + } + // Clear the lock file so that future invocations can make progress. + Locked.unsafeRemoveLockFile(); + continue; + } + } + break; + } + } + } +} diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index f8f096a39538a..b5de3c64b8a8e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -67,6 +67,8 @@ class ModuleInterfaceBuilder { version::Version &Vers, llvm::StringSaver &SubArgSaver, SmallVectorImpl &SubArgs); + bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, const SearchPathOptions &searchPathOpts, @@ -102,7 +104,8 @@ class ModuleInterfaceBuilder { } bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild = nullptr); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 4b238525dd14e..87ecf51cb370d 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -340,7 +340,7 @@ struct ModuleRebuildInfo { /// normal cache, the prebuilt cache, a module adjacent to the interface, or /// a module that we'll build from a module interface. class ModuleInterfaceLoaderImpl { - using AccessPathElem = std::pair; + using AccessPathElem = Located; friend class swift::ModuleInterfaceLoader; ASTContext &ctx; llvm::vfs::FileSystem &fs; @@ -950,13 +950,11 @@ class ModuleInterfaceLoaderImpl { std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. - // Diagnose that we didn't find a loadable module, if we were asked to. - if (remarkOnRebuildFromInterface) { + auto remarkRebuild = [&]() { rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, interfacePath); - } - + }; // If we found an out-of-date .swiftmodule, we still want to add it as // a dependency of the .swiftinterface. That way if it's updated, but // the .swiftinterface remains the same, we invalidate the cache and @@ -966,7 +964,9 @@ class ModuleInterfaceLoaderImpl { builder.addExtraDependency(modulePath); if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer)) + &moduleBuffer, + remarkOnRebuildFromInterface ? remarkRebuild: + llvm::function_ref())) return std::make_error_code(std::errc::invalid_argument); assert(moduleBuffer && @@ -1020,10 +1020,10 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } // Create an instance of the Impl to do the heavy lifting. - auto ModuleName = ModuleID.first.str(); + auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.second, + CacheDir, PrebuiltCacheDir, ModuleID.Loc, RemarkOnRebuildFromInterface, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 7fa40c3a94b5f..3369917df62b9 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -49,7 +49,7 @@ static void diagnoseScopedImports(DiagnosticEngine &diags, for (const ModuleDecl::ImportedModule &importPair : imports) { if (importPair.first.empty()) continue; - diags.diagnose(importPair.first.front().second, + diags.diagnose(importPair.first.front().Loc, diag::module_interface_scoped_import_unsupported); } } @@ -119,7 +119,7 @@ static void printImports(raw_ostream &out, ModuleDecl *M) { if (!import.first.empty()) { out << "/*"; for (const auto &accessPathElem : import.first) - out << "." << accessPathElem.first; + out << "." << accessPathElem.Item; out << "*/"; } @@ -440,7 +440,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten); + Opts.PreserveTypesAsWritten, Opts.PrintFullConvention); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 1427e7197a524..5ae5755ed69e1 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -667,7 +667,7 @@ static void countStatsOfSourceFile(UnifiedStatsReporter &Stats, SourceFile *SF) { auto &C = Stats.getFrontendCounters(); auto &SM = Instance.getSourceMgr(); - C.NumDecls += SF->Decls.size(); + C.NumDecls += SF->getTopLevelDecls().size(); C.NumLocalTypeDecls += SF->LocalTypeDecls.size(); C.NumObjCMethods += SF->ObjCMethods.size(); C.NumInfixOperators += SF->InfixOperators.size(); @@ -894,10 +894,6 @@ static SourceFile *getPrimaryOrMainSourceFile(CompilerInvocation &Invocation, /// files were specified, use them; otherwise, dump the AST to stdout. static void dumpAST(CompilerInvocation &Invocation, CompilerInstance &Instance) { - // FIXME: WMO doesn't use the notion of primary files, so this doesn't do the - // right thing. Perhaps it'd be best to ignore WMO when dumping the AST, just - // like WMO ignores `-incremental`. - auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -1047,6 +1043,59 @@ static bool writeTBDIfNeeded(CompilerInvocation &Invocation, return writeTBD(Instance.getMainModule(), TBDPath, tbdOpts); } +static std::string changeToLdAdd(StringRef ldHide) { + SmallString<64> SymbolBuffer; + llvm::raw_svector_ostream OS(SymbolBuffer); + auto Parts = ldHide.split("$hide$"); + assert(!Parts.first.empty()); + assert(!Parts.second.empty()); + OS << Parts.first << "$add$" << Parts.second; + return OS.str().str(); +} + +static bool writeLdAddCFileIfNeeded(CompilerInvocation &Invocation, + CompilerInstance &Instance) { + auto frontendOpts = Invocation.getFrontendOptions(); + if (!frontendOpts.InputsAndOutputs.isWholeModule()) + return false; + auto Path = Invocation.getLdAddCFileOutputPathForWholeModule(); + if (Path.empty()) + return false; + if (!frontendOpts.InputsAndOutputs.isWholeModule()) { + Instance.getDiags().diagnose(SourceLoc(), + diag::tbd_only_supported_in_whole_module); + return true; + } + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + llvm::StringSet<> ldSymbols; + auto *module = Instance.getMainModule(); + enumeratePublicSymbols(module, ldSymbols, tbdOpts); + std::error_code EC; + llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); + if (EC) { + module->getASTContext().Diags.diagnose(SourceLoc(), + diag::error_opening_output, + Path, EC.message()); + return true; + } + OS << "// Automatically generated C source file from the Swift compiler \n" + << "// to add removed symbols back to the high-level framework for deployment\n" + << "// targets prior to the OS version when these symbols were moved to\n" + << "// a low-level framework " << module->getName().str() << ".\n\n"; + unsigned Idx = 0; + for (auto &S: ldSymbols) { + SmallString<32> NameBuffer; + llvm::raw_svector_ostream NameOS(NameBuffer); + NameOS << "ldAdd_" << Idx; + OS << "extern const char " << NameOS.str() << " __asm(\"" << + changeToLdAdd(S.getKey()) << "\");\n"; + OS << "const char " << NameOS.str() << " = 0;\n"; + ++ Idx; + } + return false; +} + static bool performCompileStepsPostSILGen( CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, @@ -1172,6 +1221,9 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( { hadAnyError |= writeTBDIfNeeded(Invocation, Instance); } + { + hadAnyError |= writeLdAddCFileIfNeeded(Invocation, Instance); + } return hadAnyError; } @@ -1210,12 +1262,12 @@ static bool performCompile(CompilerInstance &Instance, return compileLLVMIR(Invocation, Instance, Stats); if (FrontendOptions::shouldActionOnlyParse(Action)) { - bool ParseDelayedDeclListsOnEnd = - Action == FrontendOptions::ActionType::DumpParse || - Invocation.getDiagnosticOptions().VerifyMode != DiagnosticOptions::NoVerify; + // Disable delayed parsing of type and function bodies when we've been + // asked to dump the resulting AST. + bool CanDelayBodies = Action != FrontendOptions::ActionType::DumpParse; Instance.performParseOnly(/*EvaluateConditionals*/ Action == FrontendOptions::ActionType::EmitImportedModules, - ParseDelayedDeclListsOnEnd); + CanDelayBodies); } else if (Action == FrontendOptions::ActionType::ResolveImports) { Instance.performParseAndResolveImportsOnly(); } else { @@ -1347,18 +1399,20 @@ static void generateIR(IRGenOptions &IRGenOpts, std::unique_ptr SM, StringRef OutputFilename, ModuleOrSourceFile MSF, std::unique_ptr &IRModule, llvm::GlobalVariable *&HashGlobal, - ArrayRef parallelOutputFilenames) { + ArrayRef parallelOutputFilenames, + llvm::StringSet<> &LinkerDirectives) { // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto &LLVMContext = getGlobalLLVMContext(); IRModule = MSF.is() ? performIRGeneration(IRGenOpts, *MSF.get(), std::move(SM), OutputFilename, PSPs, - LLVMContext, &HashGlobal) + LLVMContext, &HashGlobal, + &LinkerDirectives) : performIRGeneration(IRGenOpts, MSF.get(), std::move(SM), OutputFilename, PSPs, LLVMContext, parallelOutputFilenames, - &HashGlobal); + &HashGlobal, &LinkerDirectives); } static bool processCommandLineAndRunImmediately(CompilerInvocation &Invocation, @@ -1457,6 +1511,17 @@ static bool generateCode(CompilerInvocation &Invocation, EffectiveLanguageVersion, OutputFilename, Stats); } +static void collectLinkerDirectives(CompilerInvocation &Invocation, + ModuleOrSourceFile MSF, + llvm::StringSet<> &Symbols) { + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + if (MSF.is()) + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); + else + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); +} + static bool performCompileStepsPostSILGen( CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, @@ -1585,6 +1650,8 @@ static bool performCompileStepsPostSILGen( return processCommandLineAndRunImmediately( Invocation, Instance, std::move(SM), MSF, observer, ReturnValue); + llvm::StringSet<> LinkerDirectives; + collectLinkerDirectives(Invocation, MSF, LinkerDirectives); StringRef OutputFilename = PSPs.OutputFilename; std::vector ParallelOutputFilenames = Invocation.getFrontendOptions().InputsAndOutputs.copyOutputFilenames(); @@ -1592,7 +1659,7 @@ static bool performCompileStepsPostSILGen( llvm::GlobalVariable *HashGlobal; generateIR( IRGenOpts, std::move(SM), PSPs, OutputFilename, MSF, IRModule, HashGlobal, - ParallelOutputFilenames); + ParallelOutputFilenames, LinkerDirectives); // Walk the AST for indexing after IR generation. Walking it before seems // to cause miscompilation issues. @@ -1880,6 +1947,10 @@ static void printTargetInfo(CompilerInvocation &invocation, out.write_escaped(langOpts.Target.getTriple()); out << "\",\n"; + out << " \"unversionedTriple\": \""; + out.write_escaped(getUnversionedTriple(langOpts.Target).getTriple()); + out << "\",\n"; + out << " \"moduleTriple\": \""; out.write_escaped(getTargetSpecificModuleTriple(langOpts.Target).getTriple()); out << "\",\n"; diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 5b9837f424f99..82a683236668c 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -68,7 +68,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, auto accessPath = ID->getModulePath(); // only the top-level name is needed (i.e. A in A.B.C) - Modules.insert(accessPath[0].first.str()); + Modules.insert(accessPath[0].Item.str()); } // And now look in the C code we're possibly using. @@ -98,8 +98,8 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, } if (opts.ImportUnderlyingModule) { - auto underlyingModule = clangImporter->loadModule( - SourceLoc(), std::make_pair(mainModule->getName(), SourceLoc())); + auto underlyingModule = clangImporter->loadModule(SourceLoc(), + { Located(mainModule->getName(), SourceLoc()) }); if (!underlyingModule) { Context.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, diff --git a/lib/FrontendTool/ReferenceDependencies.cpp b/lib/FrontendTool/ReferenceDependencies.cpp index ef198c095afd1..c9cb5117a0a57 100644 --- a/lib/FrontendTool/ReferenceDependencies.cpp +++ b/lib/FrontendTool/ReferenceDependencies.cpp @@ -252,7 +252,7 @@ ProvidesEmitter::emitTopLevelNames() const { out << providesTopLevel << ":\n"; CollectedDeclarations cpd; - for (const Decl *D : SF->Decls) + for (const Decl *D : SF->getTopLevelDecls()) emitTopLevelDecl(D, cpd); for (auto *operatorFunction : cpd.memberOperatorDecls) out << "- \"" << escape(operatorFunction->getName()) << "\"\n"; diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 042571855a62a..40bf8dbb52dee 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp CommentConversion.cpp + CompletionInstance.cpp ConformingMethodList.cpp ExprContextAnalysis.cpp Formatting.cpp diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index f5b9b2e658429..cd6b58e40d963 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1260,11 +1260,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { Builder.addTypeAnnotation(ST.getString()); } - /// Set to true when we have delivered code completion results - /// to the \c Consumer. - bool DeliveredResults = false; - - Optional> typeCheckParsedExpr() { assert(ParsedExpr && "should have an expression"); @@ -1279,24 +1274,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { // typecheck again. rdar://21466394 if (CheckKind == CompletionTypeCheckKind::Normal && ParsedExpr->getType() && !ParsedExpr->getType()->is()) { - auto refDecl = ParsedExpr->getReferencedDecl(); - auto exprTy = ParsedExpr->getType(); - if (!refDecl) { - // FIXME: do this in the not-already-type-checked branch too? - if (auto *apply = dyn_cast(ParsedExpr)) { - refDecl = apply->getFn()->getReferencedDecl(); - } else if (auto *apply = dyn_cast(ParsedExpr)) { - // If this is an IUO, use the underlying non-optional type instead - auto fnDecl = apply->getFn()->getReferencedDecl(); - if (auto FD = fnDecl.getDecl()) { - if (FD->isImplicitlyUnwrappedOptional()) { - if (auto OT = exprTy->getOptionalObjectType()) - exprTy = OT; - } - } - } - } - return std::make_pair(exprTy, refDecl); + return getReferencedDecl(ParsedExpr); } ConcreteDeclRef ReferencedDecl = nullptr; @@ -1353,7 +1331,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { AttTargetDK = DK; } - void completeExpr() override; void completeDotExpr(Expr *E, SourceLoc DotLoc) override; void completeStmtOrExpr(CodeCompletionExpr *E) override; void completePostfixExprBeginning(CodeCompletionExpr *E) override; @@ -1377,7 +1354,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeAccessorBeginning(CodeCompletionExpr *E) override; void completePoundAvailablePlatform() override; - void completeImportDecl(std::vector> &Path) override; + void completeImportDecl(std::vector> &Path) override; void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; @@ -1399,18 +1376,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { }; } // end anonymous namespace -void CodeCompletionCallbacksImpl::completeExpr() { - if (DeliveredResults) - return; - - Parser::ParserPositionRAII RestorePosition(P); - P.restoreParserPosition(ExprBeginPosition); - - // FIXME: implement fallback code completion. - - deliverCompletionResults(); -} - namespace { static bool isTopLevelContext(const DeclContext *DC) { for (; DC && DC->isLocalContext(); DC = DC->getParent()) { @@ -1923,6 +1888,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PrintOptions PO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO)); } } @@ -1944,6 +1911,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.PrintOptionalAsImplicitlyUnwrapped = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO) + suffix); } @@ -2288,9 +2257,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (NeedComma) Builder.addComma(); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); - Builder.addCallParameter(argName, bodyName, paramTy, isVariadic, isInOut, - isIUO, isAutoclosure); + Builder.addCallParameter(argName, bodyName, paramTy, contextTy, + isVariadic, isInOut, isIUO, isAutoclosure); modifiedBuilder = true; NeedComma = true; @@ -2636,6 +2608,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); ResultType.print(OS, PO); } } @@ -3472,9 +3446,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addEqual(); builder.addWhitespace(" "); assert(RHSType && resultType); - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); addTypeAnnotation(builder, resultType); } @@ -3495,10 +3470,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addWhitespace(" "); builder.addTextChunk(op->getName().str()); builder.addWhitespace(" "); - if (RHSType) - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + if (RHSType) { + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); + } if (resultType) addTypeAnnotation(builder, resultType); } @@ -3722,21 +3699,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addFromProto(LK::ColorLiteral, "", [&](Builder &builder) { builder.addTextChunk("#colorLiteral"); builder.addLeftParen(); - builder.addCallParameter(context.getIdentifier("red"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("red"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("green"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("green"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("blue"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("blue"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("alpha"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("alpha"), floatType); builder.addRightParen(); }); @@ -3745,9 +3714,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addTextChunk("#imageLiteral"); builder.addLeftParen(); builder.addCallParameter(context.getIdentifier("resourceName"), - stringType, /*IsVarArg*/ false, - /*IsInOut*/ false, /*isIUO*/ false, - /*isAutoClosure*/ false); + stringType); builder.addRightParen(); }); @@ -4078,10 +4045,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Kind = LookupKind::ImportFromModule; NeedLeadingDot = ResultsHaveLeadingDot; - llvm::SmallVector, 1> LookupAccessPath; + llvm::SmallVector, 1> LookupAccessPath; for (auto Piece : AccessPath) { - LookupAccessPath.push_back( - std::make_pair(Ctx.getIdentifier(Piece), SourceLoc())); + LookupAccessPath.push_back({Ctx.getIdentifier(Piece), SourceLoc()}); } AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); TheModule->lookupVisibleDecls(LookupAccessPath, FilteringConsumer, @@ -4193,8 +4159,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { /// Return type if the result type if \p VD should be represented as opaque /// result type. - TypeLoc getOpaqueResultTypeLoc(const ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo) { + Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason, + DynamicLookupInfo dynamicLookupInfo) { if (Reason != DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal) return nullptr; @@ -4204,35 +4170,61 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { return nullptr; Type ResultT; - if (auto *FD = dyn_cast(VD)) + if (auto *FD = dyn_cast(VD)) { + if (FD->getGenericParams()) { + // Generic function cannot have opaque result type. + return nullptr; + } ResultT = FD->getResultInterfaceType(); - else if (auto *SD = dyn_cast(VD)) + } else if (auto *SD = dyn_cast(VD)) { + if (SD->getGenericParams()) { + // Generic subscript cannot have opaque result type. + return nullptr; + } ResultT = SD->getElementInterfaceType(); - else if (auto *VarD = dyn_cast(VD)) + } else if (auto *VarD = dyn_cast(VD)) { ResultT = VarD->getInterfaceType(); - else - return nullptr; - - if (!ResultT->is()) - // The result is not associatedtype. + } else { return nullptr; + } - // If associatedtype doesn't have conformance/superclass constraint, we - // can't use opaque type. - auto assocTyD = ResultT->castTo()->getAssocType(); - if (!assocTyD->getInherited().size()) + if (!ResultT->is() || + !ResultT->castTo()->getAssocType()) + // The result is not a valid associatedtype. return nullptr; // Try substitution to see if the associated type is resolved to concrete // type. auto substMap = currTy->getMemberSubstitutionMap( CurrDeclContext->getParentModule(), VD); - ResultT = ResultT.subst(substMap); - if (!ResultT || !ResultT->is()) + if (!ResultT.subst(substMap)->is()) // If resolved print it. return nullptr; - return assocTyD->getInherited()[0]; + auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext(); + + if (genericSig->isConcreteType(ResultT)) + // If it has same type requrement, we will emit the concrete type. + return nullptr; + + // Collect requirements on the associatedtype. + SmallVector opaqueTypes; + bool hasExplicitAnyObject = false; + if (auto superTy = genericSig->getSuperclassBound(ResultT)) + opaqueTypes.push_back(superTy); + for (auto proto : genericSig->getConformsTo(ResultT)) + opaqueTypes.push_back(proto->getDeclaredInterfaceType()); + if (auto layout = genericSig->getLayoutConstraint(ResultT)) + hasExplicitAnyObject = layout->isClass(); + + if (!hasExplicitAnyObject) { + if (opaqueTypes.empty()) + return nullptr; + if (opaqueTypes.size() == 1) + return opaqueTypes.front(); + } + return ProtocolCompositionType::get( + VD->getASTContext(), opaqueTypes, hasExplicitAnyObject); } void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, @@ -4240,14 +4232,14 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { CodeCompletionResultBuilder &Builder, bool hasDeclIntroducer) { class DeclPrinter : public StreamPrinter { - TypeLoc OpaqueBaseTy; + Type OpaqueBaseTy; public: using StreamPrinter::StreamPrinter; Optional NameOffset; - DeclPrinter(raw_ostream &OS, TypeLoc OpaqueBaseTy) + DeclPrinter(raw_ostream &OS, Type OpaqueBaseTy) : StreamPrinter(OS), OpaqueBaseTy(OpaqueBaseTy) {} void printDeclLoc(const Decl *D) override { @@ -4259,7 +4251,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override { if (!OpaqueBaseTy.isNull()) { OS << "some "; - TL = OpaqueBaseTy; + TL = TypeLoc::withoutLoc(OpaqueBaseTy); } } }; @@ -4269,10 +4261,11 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); DeclPrinter Printer( - OS, getOpaqueResultTypeLoc(VD, Reason, dynamicLookupInfo)); + OS, getOpaqueResultType(VD, Reason, dynamicLookupInfo)); PrintOptions Options; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) Options.setBaseType(transformType); + Options.SkipUnderscoredKeywords = true; Options.PrintImplicitAttrs = false; Options.ExclusiveAttrList.push_back(TAK_escaping); Options.ExclusiveAttrList.push_back(TAK_autoclosure); @@ -4391,6 +4384,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); PrintOptions Options; + if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) + Options.setBaseType(transformType); Options.PrintImplicitAttrs = false; Options.SkipAttributes = true; CD->print(OS, Options); @@ -4721,10 +4716,10 @@ void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr * } void CodeCompletionCallbacksImpl::completeImportDecl( - std::vector> &Path) { + std::vector> &Path) { Kind = CompletionKind::Import; CurDeclContext = P.CurDeclContext; - DotLoc = Path.empty() ? SourceLoc() : Path.back().second; + DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc; if (DotLoc.isInvalid()) return; auto Importer = static_cast(CurDeclContext->getASTContext(). @@ -4733,7 +4728,7 @@ void CodeCompletionCallbacksImpl::completeImportDecl( Importer->collectSubModuleNames(Path, SubNames); ASTContext &Ctx = CurDeclContext->getASTContext(); for (StringRef Sub : SubNames) { - Path.push_back(std::make_pair(Ctx.getIdentifier(Sub), SourceLoc())); + Path.push_back({ Ctx.getIdentifier(Sub), SourceLoc() }); SubModuleNameVisibilityPairs.push_back( std::make_pair(Sub.str(), Ctx.getLoadedModule(Path))); Path.pop_back(); @@ -5570,7 +5565,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { std::vector AccessPath; for (auto Piece : Path) { - AccessPath.push_back(Piece.first.str()); + AccessPath.push_back(Piece.Item.str()); } StringRef ModuleFilename = TheModule->getModuleFilename(); @@ -5645,7 +5640,6 @@ void CodeCompletionCallbacksImpl::deliverCompletionResults() { Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DCForModules); RequestedModules.clear(); - DeliveredResults = true; } void PrintingCodeCompletionConsumer::handleResults( diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 6498cc80ad6b2..cc4793d9eb1ba 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -344,7 +344,7 @@ class CodeCompletionResultBuilder { } void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, - bool IsVarArg, bool IsInOut, bool IsIUO, + Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, bool isAutoClosure) { CurrentNestingLevel++; @@ -388,6 +388,8 @@ class CodeCompletionResultBuilder { PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); std::string TypeName = Ty->getString(PO); addChunkWithText(CodeCompletionString::Chunk::ChunkKind::CallParameterType, TypeName); @@ -398,10 +400,13 @@ class CodeCompletionResultBuilder { if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallParameterClosureType. PrintOptions PO; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, AFT->getString(PO)); @@ -412,10 +417,10 @@ class CodeCompletionResultBuilder { CurrentNestingLevel--; } - void addCallParameter(Identifier Name, Type Ty, bool IsVarArg, bool IsInOut, - bool IsIUO, bool isAutoClosure) { - addCallParameter(Name, Identifier(), Ty, IsVarArg, IsInOut, IsIUO, - isAutoClosure); + void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { + addCallParameter(Name, Identifier(), Ty, ContextTy, + /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, + /*isAutoClosure=*/false); } void addGenericParameter(StringRef Name) { diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp new file mode 100644 index 0000000000000..155b72c5b859f --- /dev/null +++ b/lib/IDE/CompletionInstance.cpp @@ -0,0 +1,325 @@ +//===--- CompletionInstance.cpp -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/CompletionInstance.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/Module.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/LangOptions.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Parse/PersistentParserState.h" +#include "swift/Subsystems.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace swift; +using namespace ide; + +std::unique_ptr +swift::ide::makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + StringRef bufferIdentifier) { + + auto origBuffSize = origBuf->getBufferSize(); + if (Offset > origBuffSize) + Offset = origBuffSize; + + auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( + origBuffSize + 1, bufferIdentifier); + auto *pos = origBuf->getBufferStart() + Offset; + auto *newPos = + std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); + *newPos = '\0'; + std::copy(pos, origBuf->getBufferEnd(), newPos + 1); + + return std::unique_ptr(newBuffer.release()); +} + +namespace { +/// Returns index number of \p D in \p Decls . If it's not found, returns ~0. +template +unsigned findIndexInRange(Decl *D, const Range &Decls) { + unsigned N = 0; + for (auto I = Decls.begin(), E = Decls.end(); I != E; ++I) { + if (*I == D) + return N; + ++N; + } + return ~0U; +} + +/// Return the element at \p N in \p Decls . +template Decl *getElementAt(const Range &Decls, unsigned N) { + assert(std::distance(Decls.begin(), Decls.end()) > N); + auto I = Decls.begin(); + std::advance(I, N); + return *I; +} + +/// Find the equivalent \c DeclContext with \p DC from \p SF AST. +/// This assumes the AST which contains \p DC has exact the same structure with +/// \p SF. +/// FIXME: This doesn't support IfConfigDecl blocks. If \p DC is in an inactive +/// config block, this function returns \c false. +static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, + SourceFile *SF) { + auto *newDC = DC; + // NOTE: Shortcut for DC->getParentSourceFile() == SF case is not needed + // because they should be always different. + + // Get the index path in the current AST. + SmallVector IndexStack; + do { + auto *D = newDC->getAsDecl(); + auto *parentDC = newDC->getParent(); + unsigned N; + if (auto parentSF = dyn_cast(parentDC)) + N = findIndexInRange(D, parentSF->getTopLevelDecls()); + else if (auto parentIDC = + dyn_cast(parentDC->getAsDecl())) + N = findIndexInRange(D, parentIDC->getMembers()); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (indexpath)"); + + // Not found in the decl context tree. + // FIXME: Probably DC is in an inactive #if block. + if (N == ~0U) { + return nullptr; + } + + IndexStack.push_back(N); + newDC = parentDC; + } while (!newDC->isModuleScopeContext()); + + assert(isa(newDC) && "DC should be in a SourceFile"); + + // Query the equivalent decl context from the base SourceFile using the index + // path. + newDC = SF; + do { + auto N = IndexStack.pop_back_val(); + Decl *D; + if (auto parentSF = dyn_cast(newDC)) + D = getElementAt(parentSF->getTopLevelDecls(), N); + else if (auto parentIDC = dyn_cast(newDC->getAsDecl())) + D = getElementAt(parentIDC->getMembers(), N); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (query)"); + newDC = dyn_cast(D); + } while (!IndexStack.empty()); + + assert(newDC->getContextKind() == DC->getContextKind()); + + return newDC; +} + +} // namespace + +bool CompletionInstance::performCachedOperaitonIfPossible( + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + if (!CachedCI) + return false; + if (CachedReuseCount >= MaxASTReuseCount) + return false; + if (CachedArgHash != ArgsHash) + return false; + + auto &CI = *CachedCI; + + auto &oldState = CI.getPersistentParserState(); + if (!oldState.hasCodeCompletionDelayedDeclState()) + return false; + + auto &SM = CI.getSourceMgr(); + if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != + completionBuffer->getBufferIdentifier()) + return false; + + auto &oldInfo = oldState.getCodeCompletionDelayedDeclState(); + + // Currently, only completions within a function body is supported. + if (oldInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return false; + + auto newBufferID = SM.addMemBufferCopy(completionBuffer); + SM.setCodeCompletionPoint(newBufferID, Offset); + + // Parse the new buffer into temporary SourceFile. + LangOptions langOpts; + langOpts.DisableParserLookup = true; + TypeCheckerOptions typeckOpts; + SearchPathOptions searchPathOpts; + DiagnosticEngine Diags(SM); + std::unique_ptr Ctx( + ASTContext::get(langOpts, typeckOpts, searchPathOpts, SM, Diags)); + registerIDERequestFunctions(Ctx->evaluator); + registerTypeCheckerRequestFunctions(Ctx->evaluator); + ModuleDecl *M = ModuleDecl::create(Identifier(), *Ctx); + unsigned BufferID = SM.getCodeCompletionBufferID(); + PersistentParserState newState; + SourceFile *newSF = + new (*Ctx) SourceFile(*M, SourceFileKind::Library, BufferID, + SourceFile::ImplicitModuleImportKind::None); + newSF->enableInterfaceHash(); + parseIntoSourceFileFull(*newSF, BufferID, &newState); + // Couldn't find any completion token? + if (!newState.hasCodeCompletionDelayedDeclState()) + return false; + + auto &newInfo = newState.getCodeCompletionDelayedDeclState(); + + // The new completion must happens in function body too. + if (newInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return false; + + auto *oldSF = oldInfo.ParentContext->getParentSourceFile(); + + // If the interface has changed, AST must be refreshed. + llvm::SmallString<32> oldInterfaceHash{}; + llvm::SmallString<32> newInterfaceHash{}; + oldSF->getInterfaceHash(oldInterfaceHash); + newSF->getInterfaceHash(newInterfaceHash); + if (oldInterfaceHash != newInterfaceHash) + return false; + + DeclContext *DC = + getEquivalentDeclContextFromSourceFile(newInfo.ParentContext, oldSF); + if (!DC) + return false; + + // OK, we can perform fast completion for this. Update the orignal delayed + // decl state. + + // Construct dummy scopes. We don't need to restore the original scope + // because they are probably not 'isResolvable()' anyway. + auto &SI = oldState.getScopeInfo(); + assert(SI.getCurrentScope() == nullptr); + Scope Top(SI, ScopeKind::TopLevel); + Scope Body(SI, ScopeKind::FunctionBody); + + oldInfo.ParentContext = DC; + oldInfo.StartOffset = newInfo.StartOffset; + oldInfo.EndOffset = newInfo.EndOffset; + oldInfo.PrevOffset = newInfo.PrevOffset; + oldState.restoreCodeCompletionDelayedDeclState(oldInfo); + + auto *AFD = cast(DC); + if (AFD->isBodySkipped()) + AFD->setBodyDelayed(AFD->getBodySourceRange()); + if (DiagC) + CI.addDiagnosticConsumer(DiagC); + + CI.getDiags().diagnose(SM.getLocForOffset(BufferID, newInfo.StartOffset), + diag::completion_reusing_astcontext); + + Callback(CI); + + if (DiagC) + CI.removeDiagnosticConsumer(DiagC); + + CachedReuseCount += 1; + + return true; +} + +bool CompletionInstance::performNewOperation( + Optional ArgsHash, swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + auto TheInstance = std::make_unique(); + auto &CI = *TheInstance; + if (DiagC) + CI.addDiagnosticConsumer(DiagC); + + if (FileSystem != llvm::vfs::getRealFileSystem()) + CI.getSourceMgr().setFileSystem(FileSystem); + + Invocation.setCodeCompletionPoint(completionBuffer, Offset); + + if (CI.setup(Invocation)) { + Error = "failed to setup compiler instance"; + return false; + } + registerIDERequestFunctions(CI.getASTContext().evaluator); + + CI.performParseAndResolveImportsOnly(); + Callback(CI); + + if (DiagC) + CI.removeDiagnosticConsumer(DiagC); + + if (ArgsHash.hasValue()) { + CachedCI = std::move(TheInstance); + CachedArgHash = *ArgsHash; + CachedReuseCount = 0; + } + + return true; +} + +bool swift::ide::CompletionInstance::performOperation( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + + // Disable to build syntax tree because code-completion skips some portion of + // source text. That breaks an invariant of syntax tree building. + Invocation.getLangOptions().BuildSyntaxTree = false; + + // FIXME: ASTScopeLookup doesn't support code completion yet. + Invocation.disableASTScopeLookup(); + + if (EnableASTCaching) { + // Compute the signature of the invocation. + llvm::hash_code ArgsHash(0); + for (auto arg : Args) + ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); + + // Concurrent completions will block so that they have higher chance to use + // the cached completion instance. + std::lock_guard lock(mtx); + + if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, + Offset, DiagC, Callback)) + return true; + + if (performNewOperation(ArgsHash, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } else { + // Concurrent completions may happen in parallel when caching is disabled. + if (performNewOperation(None, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } + + assert(!Error.empty()); + return false; +} diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index d7dbf5fd1bcab..c955d2f81d184 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -111,7 +111,7 @@ void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) { // Here, 'value' is '' unless we explicitly typecheck the // 'guard' statement. SourceFile *SF = DC->getParentSourceFile(); - for (auto *D : SF->Decls) { + for (auto *D : SF->getTopLevelDecls()) { if (auto Code = dyn_cast(D)) { typeCheckTopLevelCodeDecl(Code); if (Code == TLCD) diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 89a27d52979ec..714116fce3ee1 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -564,6 +564,10 @@ class FormatWalker : public SourceEntityWalker { public: SourceLocIterator(TokenIt It) :It(It) {} SourceLocIterator(const SourceLocIterator& mit) : It(mit.It) {} + const SourceLocIterator &operator=(const SourceLocIterator &mit) { + It = mit.It; + return *this; + } SourceLocIterator& operator++() {++It; return *this;} SourceLocIterator operator++(int) { SourceLocIterator tmp(*this); diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 0b719b59c85e8..6d4050743df8f 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -85,7 +85,8 @@ PrintOptions PrintOptions::printDocInterface() { PrintOptions::ArgAndParamPrintingMode::BothAlways; result.PrintDocumentationComments = false; result.PrintRegularClangComments = false; - result.PrintFunctionRepresentationAttrs = false; + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; return result; } diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index e87deadb53b05..b061bebbfa362 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -448,7 +448,7 @@ void swift::ide::printSubmoduleInterface( auto RHSPath = RHS->getFullAccessPath(); for (unsigned i = 0, e = std::min(LHSPath.size(), RHSPath.size()); i != e; i++) { - if (int Ret = LHSPath[i].first.str().compare(RHSPath[i].first.str())) + if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str())) return Ret < 0; } return false; @@ -667,7 +667,7 @@ static SourceLoc getDeclStartPosition(SourceFile &File) { return false; }; - for (auto D : File.Decls) { + for (auto D : File.getTopLevelDecls()) { if (tryUpdateStart(D->getStartLoc())) { tryUpdateStart(D->getAttrs().getStartLoc()); auto RawComment = D->getRawComment(); diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 4d3356c440687..247ddb4d0e473 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -205,7 +205,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, Ctx.SourceMgr.setCodeCompletionPoint(*BufferID, CodeCompletionOffset); // Parse, typecheck and temporarily insert the incomplete code into the AST. - const unsigned OriginalDeclCount = SF.Decls.size(); + const unsigned OriginalDeclCount = SF.getTopLevelDecls().size(); PersistentParserState PersistentState; bool Done; @@ -218,7 +218,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, // Now we are done with code completion. Remove the declarations we // temporarily inserted. - SF.Decls.resize(OriginalDeclCount); + SF.truncateTopLevelDecls(OriginalDeclCount); // Reset the error state because it's only relevant to the code that we just // processed, which now gets thrown away. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index b148785f6d070..9ce2bc7739024 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -3166,6 +3166,97 @@ static bool rangeStartMayNeedRename(ResolvedRangeInfo Info) { } llvm_unreachable("unhandled kind"); } + +bool RefactoringActionConvertToComputedProperty:: +isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { + if (Info.Kind != RangeKind::SingleDecl) { + return false; + } + + if (Info.ContainedNodes.size() != 1) { + return false; + } + + auto D = Info.ContainedNodes[0].dyn_cast(); + if (!D) { + return false; + } + + auto Binding = dyn_cast(D); + if (!Binding) { + return false; + } + + auto SV = Binding->getSingleVar(); + if (!SV) { + return false; + } + + // willSet, didSet cannot be provided together with a getter + for (auto AD : SV->getAllAccessors()) { + if (AD->isObservingAccessor()) { + return false; + } + } + + // 'lazy' must not be used on a computed property + // NSCopying and IBOutlet attribute requires property to be mutable + auto Attributies = SV->getAttrs(); + if (Attributies.hasAttribute() || + Attributies.hasAttribute() || + Attributies.hasAttribute()) { + return false; + } + + // Property wrapper cannot be applied to a computed property + if (SV->hasAttachedPropertyWrapper()) { + return false; + } + + // has an initializer + return Binding->hasInitStringRepresentation(0); +} + +bool RefactoringActionConvertToComputedProperty::performChange() { + // Get an initialization + auto D = RangeInfo.ContainedNodes[0].dyn_cast(); + auto Binding = dyn_cast(D); + SmallString<128> scratch; + auto Init = Binding->getInitStringRepresentation(0, scratch); + + // Get type + auto SV = Binding->getSingleVar(); + auto SVType = SV->getType(); + auto TR = SV->getTypeReprOrParentPatternTypeRepr(); + + llvm::SmallString<64> DeclBuffer; + llvm::raw_svector_ostream OS(DeclBuffer); + llvm::StringRef Space = " "; + llvm::StringRef NewLine = "\n"; + + OS << tok::kw_var << Space; + // Add var name + OS << SV->getNameStr().str() << ":" << Space; + // For computed property must write a type of var + if (TR) { + OS << Lexer::getCharSourceRangeFromSourceRange(SM, TR->getSourceRange()).str(); + } else { + SVType.print(OS); + } + + OS << Space << tok::l_brace << NewLine; + // Add an initialization + OS << tok::kw_return << Space << Init.str() << NewLine; + OS << tok::r_brace; + + // Replace initializer to computed property + auto ReplaceStartLoc = Binding->getLoc(); + auto ReplaceEndLoc = Binding->getSourceRange().End; + auto ReplaceRange = SourceRange(ReplaceStartLoc, ReplaceEndLoc); + auto ReplaceCharSourceRange = Lexer::getCharSourceRangeFromSourceRange(SM, ReplaceRange); + EditConsumer.accept(SM, ReplaceCharSourceRange, DeclBuffer.str()); + return false; // success +} }// end of anonymous namespace StringRef swift::ide:: diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 156a64129b648..e551ced63b7aa 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -74,7 +74,7 @@ class SemaAnnotator : public ASTWalker { bool passReference(ValueDecl *D, Type Ty, SourceLoc Loc, SourceRange Range, ReferenceMetaData Data); bool passReference(ValueDecl *D, Type Ty, DeclNameLoc Loc, ReferenceMetaData Data); - bool passReference(ModuleEntity Mod, std::pair IdLoc); + bool passReference(ModuleEntity Mod, Located IdLoc); bool passSubscriptReference(ValueDecl *D, SourceLoc Loc, ReferenceMetaData Data, bool IsOpenBracket); @@ -270,7 +270,7 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { if (auto *DRE = dyn_cast(E)) { if (auto *module = dyn_cast(DRE->getDecl())) { if (!passReference(ModuleEntity(module), - std::make_pair(module->getName(), E->getLoc()))) + {module->getName(), E->getLoc()})) return { false, nullptr }; } else if (!passReference(DRE->getDecl(), DRE->getType(), DRE->getNameLoc(), @@ -491,7 +491,7 @@ bool SemaAnnotator::walkToTypeReprPre(TypeRepr *T) { if (ValueDecl *VD = IdT->getBoundDecl()) { if (auto *ModD = dyn_cast(VD)) { auto ident = IdT->getNameRef().getBaseIdentifier(); - return passReference(ModD, std::make_pair(ident, IdT->getLoc())); + return passReference(ModD, { ident, IdT->getLoc() }); } return passReference(VD, Type(), IdT->getNameLoc(), @@ -669,11 +669,11 @@ passReference(ValueDecl *D, Type Ty, SourceLoc BaseNameLoc, SourceRange Range, } bool SemaAnnotator::passReference(ModuleEntity Mod, - std::pair IdLoc) { - if (IdLoc.second.isInvalid()) + Located IdLoc) { + if (IdLoc.Loc.isInvalid()) return true; - unsigned NameLen = IdLoc.first.getLength(); - CharSourceRange Range{ IdLoc.second, NameLen }; + unsigned NameLen = IdLoc.Item.getLength(); + CharSourceRange Range{ IdLoc.Loc, NameLen }; bool Continue = SEWalker.visitModuleReference(Mod, Range); if (!Continue) Cancelled = true; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index bb645722236da..a60e6ab70e276 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -177,7 +177,7 @@ bool NameMatcher::handleCustomAttrs(Decl *D) { // CustomAttr's type, e.g. on `Wrapper` in `@Wrapper(wrappedValue: 10)`. SWIFT_DEFER { CustomAttrArg = None; }; if (Arg && !Arg->isImplicit()) - CustomAttrArg = {Repr->getLoc(), Arg}; + CustomAttrArg = Located(Arg, Repr->getLoc()); if (!Repr->walk(*this)) return false; } @@ -246,7 +246,7 @@ bool NameMatcher::walkToDeclPre(Decl *D) { } } else if (ImportDecl *ID = dyn_cast(D)) { for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) { - tryResolve(ASTWalker::ParentTy(D), Element.second); + tryResolve(ASTWalker::ParentTy(D), Element.Loc); if (isDone()) break; } @@ -416,9 +416,9 @@ bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isa(T)) { // If we're walking a CustomAttr's type we may have an associated call // argument to resolve with from its semantic initializer. - if (CustomAttrArg.hasValue() && CustomAttrArg->first == T->getLoc()) { + if (CustomAttrArg.hasValue() && CustomAttrArg->Loc == T->getLoc()) { tryResolve(ASTWalker::ParentTy(T), T->getLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->second, LabelRangeEndAt::BeforeElemStart)); + getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->Item, LabelRangeEndAt::BeforeElemStart)); } else { tryResolve(ASTWalker::ParentTy(T), T->getLoc()); } diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index af2e4e13d01af..faa7da133ab17 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -57,7 +57,7 @@ struct SyntaxModelContext::Implementation { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and /// number of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchImageOrFileLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 5; if (Tokens.size() < NUM_TOKENS) @@ -76,8 +76,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { if (Tokens[1].getText() != "resourceName") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } /// Matches the tokens in the argument of an image literal expression if its @@ -86,7 +85,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and number /// of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchColorLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 17; if (Tokens.size() < NUM_TOKENS) @@ -112,8 +111,7 @@ matchColorLiteralArg(ArrayRef Tokens) { Tokens[9].getText() != "blue" || Tokens[13].getText() != "alpha") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) @@ -172,9 +170,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_imageLiteral: if (auto Match = matchImageOrFileLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the extra matched tokens - I += Match->second - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } @@ -182,9 +180,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_colorLiteral: if (auto Match = matchColorLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the matches tokens - I += Match->second - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index ec7393c37633a..a52c6570e178f 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -976,3 +976,55 @@ ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) { return ClangNode(); } + +std::pair swift::ide::getReferencedDecl(Expr *expr) { + auto exprTy = expr->getType(); + + // Look through unbound instance member accesses. + if (auto *dotSyntaxExpr = dyn_cast(expr)) + expr = dotSyntaxExpr->getRHS(); + + // Look through the 'self' application. + if (auto *selfApplyExpr = dyn_cast(expr)) + expr = selfApplyExpr->getFn(); + + // Look through curry thunks. + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + if (isa(body) && + closure->getParameters()->size() == 1) + expr = closure->getSingleExpressionBody(); + } + } + + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + body = body->getSemanticsProvidingExpr(); + if (auto *outerCall = dyn_cast(body)) { + if (auto *innerCall = dyn_cast(outerCall->getFn())) { + if (auto *declRef = dyn_cast(innerCall->getFn())) { + expr = declRef; + } + } + } + } + } + + // If this is an IUO result, unwrap the optional type. + auto refDecl = expr->getReferencedDecl(); + if (!refDecl) { + if (auto *applyExpr = dyn_cast(expr)) { + auto fnDecl = applyExpr->getFn()->getReferencedDecl(); + if (auto *func = fnDecl.getDecl()) { + if (func->isImplicitlyUnwrappedOptional()) { + if (auto objectTy = exprTy->getOptionalObjectType()) + exprTy = objectTy; + } + } + } + } + + return std::make_pair(exprTy, refDecl); +} diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 65492f9625ec1..3c82701e78448 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -204,8 +204,13 @@ class ClassMetadataScanner : public ClassMetadataVisitor { addPointer(); } } - void addGenericArgument(ClassDecl *forClass) { addPointer(); } - void addGenericWitnessTable(ClassDecl *forClass) { addPointer(); } + void addGenericArgument(GenericRequirement requirement, ClassDecl *forClass) { + addPointer(); + } + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { + addPointer(); + } void addPlaceholder(MissingMemberDecl *MMD) { for (auto i : range(MMD->getNumberOfVTableEntries())) { (void)i; diff --git a/lib/IRGen/ConstantBuilder.h b/lib/IRGen/ConstantBuilder.h index 3bb9e7d1fb338..8c85f8d3c2a33 100644 --- a/lib/IRGen/ConstantBuilder.h +++ b/lib/IRGen/ConstantBuilder.h @@ -82,6 +82,11 @@ class ConstantAggregateBuilderBase void addRelativeAddress(llvm::Constant *target) { assert(!isa(target)); + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + add(llvm::ConstantExpr::getPtrToInt(target, IGM().RelativeAddressTy, false)); + return; + } addRelativeOffset(IGM().RelativeAddressTy, target); } @@ -90,6 +95,21 @@ class ConstantAggregateBuilderBase /// a "GOT-equivalent", i.e. a pointer to an external object; if so, /// set the low bit of the offset to indicate that this is true. void addRelativeAddress(ConstantReference reference) { + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + // also, we should set the lowest bit, but I don't know how to do that + // there's no GOT on WebAssembly anyways though + + + llvm::Constant *offset = llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false); + // borrowed from addTaggedRelativeOffset + unsigned tag = unsigned(reference.isIndirect()); + if (tag) { + offset = llvm::ConstantExpr::getAdd(offset, llvm::ConstantInt::get(IGM().RelativeAddressTy, tag)); + } + add(offset); + return; + } addTaggedRelativeOffset(IGM().RelativeAddressTy, reference.getValue(), unsigned(reference.isIndirect())); @@ -99,6 +119,11 @@ class ConstantAggregateBuilderBase /// The target must be a "GOT-equivalent", i.e. a pointer to an /// external object. void addIndirectRelativeAddress(ConstantReference reference) { + if (IGM().TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + // WebAssembly: hack: doesn't support PCrel data relocations + add(llvm::ConstantExpr::getPtrToInt(reference.getValue(), IGM().RelativeAddressTy, false)); + return; + } assert(reference.isIndirect()); addRelativeOffset(IGM().RelativeAddressTy, reference.getValue()); diff --git a/lib/IRGen/EnumMetadataVisitor.h b/lib/IRGen/EnumMetadataVisitor.h index 30bd8da890121..ab6690c42dac8 100644 --- a/lib/IRGen/EnumMetadataVisitor.h +++ b/lib/IRGen/EnumMetadataVisitor.h @@ -82,8 +82,8 @@ class EnumMetadataScanner : public EnumMetadataVisitor { void addMetadataFlags() { addPointer(); } void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void addPayloadSize() { addPointer(); } void noteStartOfTypeSpecificMembers() {} diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 96665c5bd7db4..8dbfb7847f052 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -692,7 +692,7 @@ namespace { case clang::Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) \ case clang::Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("canonical or dependent type in ABI lowering"); // These shouldn't occur in expandable struct types. diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index f572e631c7ad8..799cb7022b2e8 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -431,7 +431,7 @@ void IRGenModule::emitSourceFile(SourceFile &SF) { llvm::SaveAndRestore SetCurSourceFile(CurSourceFile, &SF); // Emit types and other global decls. - for (auto *decl : SF.Decls) + for (auto *decl : SF.getTopLevelDecls()) emitGlobalDecl(decl); for (auto *localDecl : SF.LocalTypeDecls) emitGlobalDecl(localDecl); @@ -788,6 +788,17 @@ IRGenModule::getAddrOfContextDescriptorForParent(DeclContext *parent, LLVM_FALLTHROUGH; case DeclContextKind::Module: + if (auto *D = ofChild->getAsDecl()) { + // If the top-level decl has been marked as moved from another module, + // using @_originallyDefinedIn, we should emit the original module as + // the context because all run-time names of this decl are based on the + // original module name. + auto OriginalModule = D->getAlternateModuleName(); + if (!OriginalModule.empty()) { + return {getAddrOfOriginalModuleContextDescriptor(OriginalModule), + ConstantReference::Direct}; + } + } return {getAddrOfModuleContextDescriptor(cast(parent)), ConstantReference::Direct}; } @@ -998,7 +1009,7 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) { return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping; } -void IRGenerator::emitGlobalTopLevel() { +void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { // Generate order numbers for the functions in the SIL module that // correspond to definitions in the LLVM module. unsigned nextOrderNumber = 0; @@ -1018,7 +1029,11 @@ void IRGenerator::emitGlobalTopLevel() { CurrentIGMPtr IGM = getGenModule(wt.getProtocol()->getDeclContext()); ensureRelativeSymbolCollocation(wt); } - + if (linkerDirectives) { + for (auto &entry: *linkerDirectives) { + createLinkerDirectiveVariable(*PrimaryIGM, entry.getKey()); + } + } for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) { Decl *decl = v.getDecl(); CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr); @@ -1076,7 +1091,7 @@ void IRGenModule::finishEmitAfterTopLevel() { if (DebugInfo) { if (ModuleDecl *TheStdlib = Context.getStdlibModule()) { if (TheStdlib != getSwiftModule()) { - std::pair AccessPath[] = { + Located AccessPath[] = { { Context.StdlibModuleName, swift::SourceLoc() } }; @@ -1114,6 +1129,7 @@ void IRGenerator::emitTypeMetadataRecords() { /// else) that we require. void IRGenerator::emitLazyDefinitions() { while (!LazyTypeMetadata.empty() || + !LazySpecializedTypeMetadataRecords.empty() || !LazyTypeContextDescriptors.empty() || !LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() || @@ -1130,6 +1146,12 @@ void IRGenerator::emitLazyDefinitions() { CurrentIGMPtr IGM = getGenModule(type->getDeclContext()); emitLazyTypeMetadata(*IGM.get(), type); } + while (!LazySpecializedTypeMetadataRecords.empty()) { + CanType type = LazySpecializedTypeMetadataRecords.pop_back_val(); + auto *nominal = type->getNominalOrBoundGenericNominal(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazySpecializedGenericTypeMetadata(*IGM.get(), type); + } while (!LazyTypeContextDescriptors.empty()) { NominalTypeDecl *type = LazyTypeContextDescriptors.pop_back_val(); auto &entry = LazyTypeGlobals.find(type)->second; @@ -1170,6 +1192,12 @@ void IRGenerator::emitLazyDefinitions() { } } + while (!LazyMetadataAccessors.empty()) { + NominalTypeDecl *nominal = LazyMetadataAccessors.pop_back_val(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazyMetadataAccessor(*IGM.get(), nominal); + } + FinishedEmittingLazyDefinitions = true; } @@ -1347,6 +1375,18 @@ void IRGenerator::noteUseOfFieldDescriptor(NominalTypeDecl *type) { LazyFieldDescriptors.push_back(type); } +void IRGenerator::noteUseOfSpecializedGenericTypeMetadata(CanType type) { + auto key = type->getAnyNominal(); + assert(key); + auto &enqueuedSpecializedTypes = this->SpecializationsForGenericTypes[key]; + if (llvm::all_of(enqueuedSpecializedTypes, + [&](CanType enqueued) { return enqueued != type; })) { + assert(!FinishedEmittingLazyDefinitions); + this->LazySpecializedTypeMetadataRecords.push_back(type); + enqueuedSpecializedTypes.push_back(type); + } +} + void IRGenerator::noteUseOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaque) { if (!opaque) return; @@ -1929,6 +1969,49 @@ llvm::GlobalVariable *swift::irgen::createVariable( return var; } +llvm::GlobalVariable * +swift::irgen::createLinkerDirectiveVariable(IRGenModule &IGM, StringRef name) { + + // A prefix of \1 can avoid further mangling of the symbol (prefixing _). + llvm::SmallString<32> NameWithFlag; + NameWithFlag.push_back('\1'); + NameWithFlag.append(name); + name = NameWithFlag.str(); + static const uint8_t Size = 8; + static const uint8_t Alignment = 8; + + // Use a char type as the type for this linker directive. + auto ProperlySizedIntTy = SILType::getBuiltinIntegerType( + Size, IGM.getSwiftModule()->getASTContext()); + auto storageType = IGM.getStorageType(ProperlySizedIntTy); + + llvm::GlobalValue *existingValue = IGM.Module.getNamedGlobal(name); + if (existingValue) { + auto existingVar = dyn_cast(existingValue); + if (existingVar && isPointerTo(existingVar->getType(), storageType)) + return existingVar; + + IGM.error(SourceLoc(), + "program too clever: variable collides with existing symbol " + + name); + + // Note that this will implicitly unique if the .unique name is also taken. + existingValue->setName(name + ".unique"); + } + + llvm::GlobalValue::LinkageTypes Linkage = + llvm::GlobalValue::LinkageTypes::ExternalLinkage; + auto var = new llvm::GlobalVariable(IGM.Module, storageType, /*constant*/true, + Linkage, /*Init to zero*/llvm::Constant::getNullValue(storageType), name); + ApplyIRLinkage({Linkage, + llvm::GlobalValue::VisibilityTypes::DefaultVisibility, + llvm::GlobalValue::DLLStorageClassTypes::DefaultStorageClass}).to(var); + var->setAlignment(Alignment); + disableAddressSanitizer(IGM, var); + IGM.addUsedGlobal(var); + return var; +} + void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var) { // Add an operand to llvm.asan.globals blacklisting this global variable. llvm::Metadata *metadata[] = { @@ -2999,6 +3082,11 @@ IRGenModule::emitDirectRelativeReference(llvm::Constant *target, // Convert the target to an integer. auto targetAddr = llvm::ConstantExpr::getPtrToInt(target, SizeTy); + // WebAssembly hack: WebAssembly doesn't support PC-relative references + if (TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + return targetAddr; + } + SmallVector indices; indices.push_back(llvm::ConstantInt::get(Int32Ty, 0)); for (unsigned baseIndex : baseIndices) { @@ -3588,8 +3676,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, if (nominal) addRuntimeResolvableType(nominal); - // Don't define the alias for foreign type metadata, since it's not ABI. - if (nominal && requiresForeignTypeMetadata(nominal)) + // Don't define the alias for foreign type metadata or prespecialized generic + // metadata, since neither is ABI. + if ((nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext())) return var; // For concrete metadata, declare the alias to its address point. @@ -3624,9 +3715,13 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, llvm::Type *defaultVarTy; unsigned adjustmentIndex; - + + bool fullMetadata = (nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext()); + // Foreign classes reference the full metadata with a GEP. - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { defaultVarTy = FullTypeMetadataStructTy; adjustmentIndex = MetadataAdjustmentIndex::ValueType; // The symbol for other nominal type metadata is generated at the address @@ -3651,10 +3746,18 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, IRGen.noteUseOfTypeMetadata(nominal); } + if (shouldPrespecializeGenericMetadata()) { + if (auto nominal = concreteType->getAnyNominal()) { + if (nominal->isGenericContext()) { + IRGen.noteUseOfSpecializedGenericTypeMetadata(concreteType); + } + } + } + Optional entity; DebugTypeInfo DbgTy; - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { entity = LinkEntity::forTypeMetadata(concreteType, TypeMetadataAddress::FullMetadata); DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), @@ -3971,10 +4074,6 @@ Optional IRGenModule::getAddrOfIVarInitDestroy( llvm::Function *IRGenModule::getAddrOfValueWitness(CanType abstractType, ValueWitness index, ForDefinition_t forDefinition) { - // We shouldn't emit value witness symbols for generic type instances. - assert(!isa(abstractType) && - "emitting value witness for generic type instance?!"); - LinkEntity entity = LinkEntity::forValueWitness(abstractType, index); llvm::Function *&entry = GlobalFuncs[entity]; diff --git a/lib/IRGen/GenDecl.h b/lib/IRGen/GenDecl.h index aabd2448096fd..93656316c6a0c 100644 --- a/lib/IRGen/GenDecl.h +++ b/lib/IRGen/GenDecl.h @@ -52,6 +52,9 @@ namespace irgen { Optional DebugLoc = None, StringRef DebugName = StringRef(), bool heapAllocated = false); + llvm::GlobalVariable * + createLinkerDirectiveVariable(IRGenModule &IGM, StringRef Name); + void disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var); } } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 1844cf3fc0134..12675741c3c9c 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -18,9 +18,10 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/Attr.h" #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" -#include "swift/AST/Attr.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SubstitutionMap.h" @@ -31,13 +32,13 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/TypeLowering.h" #include "swift/Strings.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclObjC.h" #include "Address.h" #include "Callee.h" @@ -53,6 +54,7 @@ #include "GenPoly.h" #include "GenStruct.h" #include "GenValueWitness.h" +#include "GenericArguments.h" #include "HeapTypeInfo.h" #include "IRGenDebugInfo.h" #include "IRGenMangler.h" @@ -824,6 +826,7 @@ namespace { auto witness = entry.getAssociatedTypeWitness().Witness->mapTypeOutOfContext(); return IGM.getAssociatedTypeWitness(witness, + Proto->getGenericSignature(), /*inProtocolContext=*/true); } @@ -1593,8 +1596,7 @@ namespace { // TargetRelativeDirectPointer SuperclassType; if (auto superclassType = getType()->getSuperclass()) { GenericSignature genericSig = getType()->getGenericSignature(); - B.addRelativeAddress(IGM.getTypeRef(superclassType->getCanonicalType(), - genericSig, + B.addRelativeAddress(IGM.getTypeRef(superclassType, genericSig, MangledTypeRefRole::Metadata) .first); } else { @@ -1834,6 +1836,34 @@ void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) { } } +void irgen::emitLazyMetadataAccessor(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericArguments genericArgs; + genericArgs.collectTypes(IGM, nominal); + + llvm::Function *accessor = IGM.getAddrOfGenericTypeMetadataAccessFunction( + nominal, genericArgs.Types, ForDefinition); + + if (IGM.getOptions().optimizeForSize()) + accessor->addFnAttr(llvm::Attribute::NoInline); + + bool isReadNone = (genericArgs.Types.size() <= + NumDirectGenericTypeMetadataAccessFunctionArgs); + + emitCacheAccessFunction( + IGM, accessor, /*cache*/ nullptr, CacheStrategy::None, + [&](IRGenFunction &IGF, Explosion ¶ms) { + return emitGenericTypeMetadataAccessFunction(IGF, params, nominal, + genericArgs); + }, + isReadNone); +} + +void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, + CanType type) { + emitSpecializedGenericStructMetadata(IGM, type); +} + llvm::Constant * IRGenModule::getAddrOfSharedContextDescriptor(LinkEntity entity, ConstantInit definition, @@ -1908,6 +1938,13 @@ IRGenModule::getAddrOfAnonymousContextDescriptor( [&]{ AnonymousContextDescriptorBuilder(*this, DC).emit(); }); } +llvm::Constant * +IRGenModule::getAddrOfOriginalModuleContextDescriptor(StringRef Name) { + return getAddrOfModuleContextDescriptor(OriginalModules.insert({Name, + ModuleDecl::create(Context.getIdentifier(Name), Context)}) + .first->getValue()); +} + static void emitInitializeFieldOffsetVector(IRGenFunction &IGF, SILType T, llvm::Value *metadata, @@ -2862,11 +2899,13 @@ namespace { llvm_unreachable("Fixed class metadata cannot have missing members"); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic parameters"); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic requirements"); } }; @@ -2901,12 +2940,14 @@ namespace { } } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.TypeMetadataPtrTy); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.WitnessTablePtrTy); } @@ -3458,8 +3499,12 @@ namespace { return descriptor; } + llvm::Constant *getNominalTypeDescriptor() { + return emitNominalTypeDescriptor(); + } + void addNominalTypeDescriptor() { - B.add(emitNominalTypeDescriptor()); + B.add(asImpl().getNominalTypeDescriptor()); } ConstantReference emitValueWitnessTable(bool relativeReference) { @@ -3467,14 +3512,18 @@ namespace { return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); } + ConstantReference getValueWitnessTable(bool relativeReference) { + return emitValueWitnessTable(relativeReference); + } + void addValueWitnessTable() { - B.add(emitValueWitnessTable(false).getValue()); + B.add(asImpl().getValueWitnessTable(false).getValue()); } void addFieldOffset(VarDecl *var) { assert(var->hasStorage() && "storing field offset for computed property?!"); - SILType structType = getLoweredType(); + SILType structType = asImpl().getLoweredType(); llvm::Constant *offset = emitPhysicalStructMemberFixedOffset(IGM, structType, var); @@ -3492,13 +3541,29 @@ namespace { B.addAlignmentPadding(super::IGM.getPointerAlignment()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } + + bool hasTrailingFlags() { + return IGM.shouldPrespecializeGenericMetadata(); + } + + void addTrailingFlags() { + auto flags = asImpl().getTrailingFlags(); + + B.addInt(IGM.Int64Ty, flags.getOpaqueValue()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags; + + return flags; + } }; class StructMetadataBuilder : @@ -3571,6 +3636,16 @@ namespace { return StructContextDescriptorBuilder(IGM, Target, RequireMetadata).emit(); } + GenericMetadataPatternFlags getPatternFlags() { + auto flags = super::getPatternFlags(); + + if (IGM.shouldPrespecializeGenericMetadata()) { + flags.setHasTrailingFlags(true); + } + + return flags; + } + ConstantReference emitValueWitnessTable(bool relativeReference) { assert(relativeReference && "should only relative reference"); return getValueWitnessTableForGenericValueType(IGM, Target, @@ -3630,10 +3705,98 @@ namespace { vectorSize }; } + void addTrailingFlags() { this->B.addInt(IGM.Int64Ty, 0); } + bool hasCompletionFunction() { return !isa(IGM.getTypeInfo(getLoweredType())); } }; + + class SpecializedGenericStructMetadataBuilder + : public StructMetadataBuilderBase< + SpecializedGenericStructMetadataBuilder> { + using super = + StructMetadataBuilderBase; + CanType type; + bool HasUnfilledFieldOffset = false; + + protected: + using super::asImpl; + using super::getLoweredType; + using super::IGM; + using super::Target; + + public: + SpecializedGenericStructMetadataBuilder(IRGenModule &IGM, CanType type, + StructDecl &decl, + ConstantStructBuilder &B) + : super(IGM, &decl, B), type(type) {} + + void noteStartOfTypeSpecificMembers() {} + + llvm::Constant *getNominalTypeDescriptor() { + return IGM.getAddrOfTypeContextDescriptor(Target, RequireMetadata); + } + + SILType getLoweredType() { return SILType::getPrimitiveObjectType(type); } + + ConstantReference emitValueWitnessTable(bool relativeReference) { + return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); + } + + ConstantReference getValueWitnessTable(bool relativeReference) { + return emitValueWitnessTable(relativeReference); + } + + void addGenericArgument(GenericRequirement requirement) { + auto t = requirement.TypeParameter.subst(genericSubstitutions()); + ConstantReference ref = IGM.getAddrOfTypeMetadata( + CanType(t), SymbolReferenceKind::Relative_Direct); + B.add(ref.getDirectValue()); + } + + void addGenericWitnessTable(GenericRequirement requirement) { + auto conformance = genericSubstitutions().lookupConformance( + requirement.TypeParameter->getCanonicalType(), requirement.Protocol); + ProtocolConformance *concreteConformance = conformance.getConcrete(); + + llvm::Constant *addr; + + Type argument = requirement.TypeParameter.subst(genericSubstitutions()); + auto argumentNominal = argument->getAnyNominal(); + if (argumentNominal && argumentNominal->isGenericContext()) { + // TODO: Statically specialize the witness table pattern for t's + // conformance. + llvm_unreachable("Statically specializing metadata at generic types is " + "not supported."); + } else { + RootProtocolConformance *rootConformance = + concreteConformance->getRootConformance(); + addr = IGM.getAddrOfWitnessTable(rootConformance); + } + + B.add(addr); + } + + SubstitutionMap genericSubstitutions() { + return type->getContextSubstitutionMap(IGM.getSwiftModule(), + type->getAnyNominal()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags = super::getTrailingFlags(); + + flags.setIsStaticSpecialization(true); + flags.setIsCanonicalStaticSpecialization(true); + + return flags; + } + + void flagUnfilledFieldOffset() { HasUnfilledFieldOffset = true; } + + bool canBeConstant() { return !HasUnfilledFieldOffset; } + }; + } // end anonymous namespace /// Emit the type metadata or metadata template for a struct. @@ -3667,6 +3830,27 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { init.finishAndCreateFuture()); } +void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, + CanType type) { + Type ty = type.getPointer(); + auto &context = type->getNominalOrBoundGenericNominal()->getASTContext(); + PrettyStackTraceType stackTraceRAII( + context, "emitting prespecialized metadata for", ty); + ConstantInitBuilder initBuilder(IGM); + auto init = initBuilder.beginStruct(); + init.setPacked(true); + + bool isPattern = false; + + auto &decl = *type.getStructOrBoundGenericStruct(); + SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init); + builder.layout(); + + bool canBeConstant = builder.canBeConstant(); + IGM.defineTypeMetadata(type, isPattern, canBeConstant, + init.finishAndCreateFuture()); +} + // Enums static Optional getConstantPayloadSize(IRGenModule &IGM, @@ -3726,11 +3910,11 @@ namespace { B.add(emitNominalTypeDescriptor()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } }; @@ -4334,6 +4518,17 @@ GenericRequirementsMetadata irgen::addGenericRequirements( unsigned tag = unsigned(descriptorRef.isIndirect()); if (protocol->isObjC()) tag |= 0x02; + // WebAssembly: hack: Wasm doesn't support PC-relative offsets. + // also doesn't handle tag yet + if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + llvm::Constant *offset = llvm::ConstantExpr::getPtrToInt(descriptorRef.getValue(), IGM.RelativeAddressTy, false); + // borrowed from addTaggedRelativeOffset + if (tag) { + offset = llvm::ConstantExpr::getAdd(offset, llvm::ConstantInt::get(IGM.RelativeAddressTy, tag)); + } + B.add(offset); + return; + } B.addTaggedRelativeOffset(IGM.RelativeAddressTy, descriptorRef.getValue(), diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 4d56cc52f7a80..f62e2995946f1 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -71,6 +71,10 @@ namespace irgen { /// generated definitions. void emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type); + /// Emit the type metadata accessor for a type for which it might be used. + void emitLazyMetadataAccessor(IRGenModule &IGM, NominalTypeDecl *type); + + void emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type); /// Emit metadata for a foreign struct, enum or class. void emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl); @@ -81,6 +85,8 @@ namespace irgen { /// Emit the metadata associated with the given enum declaration. void emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum); + void emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type); + /// Get what will be the index into the generic type argument array at the end /// of a nominal type's metadata. int32_t getIndexOfGenericArgument(IRGenModule &IGM, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index e61ad1c1674e4..e237867eb812a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -828,6 +828,15 @@ bool IRGenModule::isResilientConformance( conformanceModule == conformance->getProtocol()->getParentModule()) return false; + // If the protocol WAS from the current module (@_originallyDefinedIn), we + // consider the conformance non-resilient, because we used to consider it + // non-resilient before the symbol moved. This is to ensure ABI stability + // across module boundaries. + if (conformanceModule == getSwiftModule() && + conformanceModule->getName().str() == + conformance->getProtocol()->getAlternateModuleName()) + return false; + // If the protocol and the conformance are in the same module and the // conforming type is not generic, they're not resilient. // @@ -1294,7 +1303,10 @@ class AccessorConformanceInfo : public ConformanceInfo { auto associate = Conformance.getTypeWitness(requirement.getAssociation()); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + Conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); Table.addBitCast(witness, IGM.Int8PtrTy); } @@ -1427,6 +1439,7 @@ void WitnessTableBuilder::build() { } llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, + GenericSignature sig, bool inProtocolContext) { // FIXME: If we can directly reference constant type metadata, do so. @@ -1435,7 +1448,7 @@ llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, auto role = inProtocolContext ? MangledTypeRefRole::DefaultAssociatedTypeWitness : MangledTypeRefRole::Metadata; - auto typeRef = getTypeRef(type, /*generic signature*/nullptr, role).first; + auto typeRef = getTypeRef(type, sig, role).first; // Set the low bit to indicate that this is a mangled name. auto witness = llvm::ConstantExpr::getPtrToInt(typeRef, IntPtrTy); @@ -1604,7 +1617,10 @@ void WitnessTableBuilder::collectResilientWitnesses( auto associate = conformance.getTypeWitness(assocType); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); resilientWitnesses.push_back(witness); continue; } diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 4415b5b6d6e86..558439d743391 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -671,7 +671,10 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { }; class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { - const uint32_t fieldRecordSize = 12; +public: + static const uint32_t FieldRecordSize = 12; + +private: const NominalTypeDecl *NTD; void addFieldDecl(const ValueDecl *value, Type type, @@ -714,7 +717,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); auto properties = NTD->getStoredProperties(); B.addInt32(properties.size()); @@ -737,7 +740,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(strategy.getElementsWithPayload().size() + strategy.getElementsWithNoPayload().size()); @@ -764,7 +767,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { else Kind = FieldDescriptorKind::Protocol; B.addInt16(uint16_t(Kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(0); } @@ -825,6 +828,57 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } }; +static bool +deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(IRGenModule &IGM) { + auto target = IGM.Context.LangOpts.Target; + + if (target.isMacOSX() && target.isMacOSXVersionLT(10, 16, 0)) { + return true; + } + if (target.isiOS() && target.isOSVersionLT(14)) { // includes tvOS + return true; + } + if (target.isWatchOS() && target.isOSVersionLT(7)) { + return true; + } + + return false; +} + +/// Metadata builder that emits a fixed-layout empty type as an empty struct, as +/// a workaround for a RemoteMirror crash in older OSes. +class EmptyStructMetadataBuilder : public ReflectionMetadataBuilder { + const NominalTypeDecl *NTD; + + void layout() override { + addNominalRef(NTD); + B.addInt32(0); + B.addInt16(uint16_t(FieldDescriptorKind::Struct)); + B.addInt16(FieldTypeMetadataBuilder::FieldRecordSize); + B.addInt32(0); + } + +public: + EmptyStructMetadataBuilder(IRGenModule &IGM, + const NominalTypeDecl *NTD) + : ReflectionMetadataBuilder(IGM), NTD(NTD) { + assert(IGM.getTypeInfoForUnlowered( + NTD->getDeclaredTypeInContext()->getCanonicalType()) + .isKnownEmpty(ResilienceExpansion::Maximal) + && "should only be used for known empty types"); + } + + llvm::GlobalVariable *emit() { + auto section = IGM.getFieldTypeMetadataSectionName(); + return ReflectionMetadataBuilder::emit( + [&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* { + return IGM.getAddrOfReflectionFieldDescriptor( + NTD->getDeclaredType()->getCanonicalType(), definition); + }, + section); + } +}; + class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder { ModuleDecl *module; CanType type; @@ -1338,6 +1392,19 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) { } if (needsOpaqueDescriptor) { + // Work around an issue in the RemoteMirror library that ships in + // macOS 10.15/iOS 13 and earlier that causes it to crash on a + // BuiltinTypeDescriptor with zero size. If the type has zero size, emit it + // as an empty struct instead, which will have the same impact on the + // encoded type layout. + auto &TI = getTypeInfoForUnlowered(T); + if (deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(*this) + && TI.isKnownEmpty(ResilienceExpansion::Maximal)) { + EmptyStructMetadataBuilder builder(*this, D); + builder.emit(); + return; + } + FixedTypeMetadataBuilder builder(*this, D); builder.emit(); } diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 447df5d61fbd7..b8be36434f023 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -198,15 +198,11 @@ void LoadableTypeInfo::addScalarToAggLowering(IRGenModule &IGM, offset.asCharUnits() + storageSize.asCharUnits()); } -static llvm::Constant *asSizeConstant(IRGenModule &IGM, Size size) { - return llvm::ConstantInt::get(IGM.SizeTy, size.getValue()); -} - llvm::Value *FixedTypeInfo::getSize(IRGenFunction &IGF, SILType T) const { return FixedTypeInfo::getStaticSize(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticSize(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedSize()); + return IGM.getSize(getFixedSize()); } llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, @@ -214,7 +210,7 @@ llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, return FixedTypeInfo::getStaticAlignmentMask(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticAlignmentMask(IRGenModule &IGM) const { - return asSizeConstant(IGM, Size(getFixedAlignment().getValue() - 1)); + return IGM.getSize(Size(getFixedAlignment().getValue() - 1)); } llvm::Value *FixedTypeInfo::getStride(IRGenFunction &IGF, SILType T) const { @@ -229,7 +225,7 @@ llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) c isBitwiseTakable(ResilienceExpansion::Maximal) == IsBitwiseTakable); } llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedStride()); + return IGM.getSize(getFixedStride()); } llvm::Value *FixedTypeInfo::isDynamicallyPackedInline(IRGenFunction &IGF, @@ -399,7 +395,7 @@ static llvm::Value *computeExtraTagBytes(IRGenFunction &IGF, IRBuilder &Builder, } auto *entryBB = Builder.GetInsertBlock(); - llvm::Value *size = asSizeConstant(IGM, fixedSize); + llvm::Value *size = IGM.getSize(fixedSize); auto *returnBB = llvm::BasicBlock::Create(Ctx); size = Builder.CreateZExtOrTrunc(size, int32Ty); // We know size < 4. diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 05024f6abd84e..d3b77b9625e73 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1023,15 +1023,21 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, return {}; } +llvm::Constant * +IRGenModule::getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init) { + if (auto known = + getAddrOfKnownValueWitnessTable(*this, concreteType, false)) { + return known.getValue(); + } + return getAddrOfValueWitnessTable(concreteType); +} + /// Emit a value-witness table for the given type. ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM, CanType abstractType, bool isPattern, bool relativeReference) { - // We shouldn't emit global value witness tables for generic type instances. - assert(!isa(abstractType) && - "emitting VWT for generic instance"); - // See if we can use a prefab witness table from the runtime. if (!isPattern) { if (auto known = getAddrOfKnownValueWitnessTable(IGM, abstractType, diff --git a/lib/IRGen/GenericArguments.h b/lib/IRGen/GenericArguments.h new file mode 100644 index 0000000000000..5365888e7cedd --- /dev/null +++ b/lib/IRGen/GenericArguments.h @@ -0,0 +1,98 @@ +//===--- MetadataRequest.cpp - IR generation for metadata requests --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements IR generation for accessing metadata. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IRGEN_GENERICARGUMENTS_H +#define SWIFT_IRGEN_GENERICARGUMENTS_H + +#include "Explosion.h" +#include "FixedTypeInfo.h" +#include "GenArchetype.h" +#include "GenClass.h" +#include "GenMeta.h" +#include "GenProto.h" +#include "GenType.h" +#include "GenericRequirement.h" +#include "IRGenDebugInfo.h" +#include "IRGenFunction.h" +#include "IRGenMangler.h" +#include "IRGenModule.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IRGenOptions.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/IRGen/Linking.h" +#include "llvm/ADT/STLExtras.h" + +namespace swift { +namespace irgen { + +/// A structure for collecting generic arguments for emitting a +/// nominal metadata reference. The structure produced here is +/// consumed by swift_getGenericMetadata() and must correspond to +/// the fill operations that the compiler emits for the bound decl. +struct GenericArguments { + /// The values to use to initialize the arguments structure. + SmallVector Values; + SmallVector Types; + + static unsigned getNumGenericArguments(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + return requirements.getNumTypeRequirements(); + } + + void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + collectTypes(IGM, requirements); + } + + void collectTypes(IRGenModule &IGM, + const GenericTypeRequirements &requirements) { + for (auto &requirement : requirements.getRequirements()) { + if (requirement.Protocol) { + Types.push_back(IGM.WitnessTablePtrTy); + } else { + Types.push_back(IGM.TypeMetadataPtrTy); + } + } + } + + void collect(IRGenFunction &IGF, CanType type) { + auto *decl = type.getNominalOrBoundGenericNominal(); + GenericTypeRequirements requirements(IGF.IGM, decl); + + auto subs = type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); + requirements.enumerateFulfillments( + IGF.IGM, subs, + [&](unsigned reqtIndex, CanType type, ProtocolConformanceRef conf) { + if (conf) { + Values.push_back(emitWitnessTableRef(IGF, type, conf)); + } else { + Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); + } + }); + + collectTypes(IGF.IGM, decl); + assert(Types.size() == Values.size()); + } +}; + +} // namespace irgen +} // namespace swift + +#endif diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 16dac3169c5c2..c554415dd6f27 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -165,6 +165,13 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { auto *Clang = static_cast(Ctx.getClangModuleLoader()); clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); + + // WebAssembly doesn't support atomics or DWARF5 yet. + if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm()) { + TargetOpts.DebuggerTuning = llvm::DebuggerKind::Default; + TargetOpts.ThreadModel = llvm::ThreadModel::Single; + } + return std::make_tuple(TargetOpts, ClangOpts.CPU, ClangOpts.Features, ClangOpts.Triple); } @@ -858,7 +865,8 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, SourceFile *SF = nullptr, - llvm::GlobalVariable **outModuleHash = nullptr) { + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *linkerDirectives = nullptr) { auto &Ctx = M->getASTContext(); assert(!Ctx.hadError()); @@ -879,8 +887,9 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, { FrontendStatsTracer tracer(Ctx.Stats, "IRGen"); + // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); if (SF) { IGM.emitSourceFile(*SF); @@ -1068,7 +1077,8 @@ struct LLVMCodeGenThreads { static void performParallelIRGeneration( IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, int numThreads, - ArrayRef outputFilenames) { + ArrayRef outputFilenames, + llvm::StringSet<> *linkerDirectives) { IRGenerator irgen(Opts, *SILMod); @@ -1134,7 +1144,7 @@ static void performParallelIRGeneration( } // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); for (auto *File : M->getFiles()) { if (auto *SF = dyn_cast(File)) { @@ -1262,18 +1272,21 @@ std::unique_ptr swift::performIRGeneration( StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { if (SILMod->getOptions().shouldPerformIRGenerationInParallel() && !parallelOutputFilenames.empty()) { auto NumThreads = SILMod->getOptions().NumThreads; ::performParallelIRGeneration(Opts, M, std::move(SILMod), ModuleName, - NumThreads, parallelOutputFilenames); + NumThreads, parallelOutputFilenames, + LinkerDirectives); // TODO: Parallel LLVM compilation cannot be used if a (single) module is // needed as return value. return nullptr; } return ::performIRGeneration(Opts, M, std::move(SILMod), ModuleName, PSPs, - LLVMContext, nullptr, outModuleHash); + LLVMContext, nullptr, outModuleHash, + LinkerDirectives); } std::unique_ptr swift:: @@ -1281,10 +1294,11 @@ performIRGeneration(IRGenOptions &Opts, SourceFile &SF, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { return ::performIRGeneration(Opts, SF.getParentModule(), std::move(SILMod), ModuleName, PSPs, LLVMContext, &SF, - outModuleHash); + outModuleHash, LinkerDirectives); } void diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 5db708ee2d57d..06f85871b7b36 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -91,7 +91,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, CanSymbolicReferenceLocally(CanSymbolicReference); AllowSymbolicReferences = true; - CanSymbolicReference = [&IGM](SymbolicReferent s) -> bool { + CanSymbolicReference = [](SymbolicReferent s) -> bool { if (auto type = s.dyn_cast()) { // The short-substitution types in the standard library have compact // manglings already, and the runtime ought to have a lookup table for diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 56d802ed1f3d1..49ed490b884d0 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -50,6 +50,7 @@ #include "llvm/Support/MD5.h" #include "ConformanceDescription.h" +#include "GenDecl.h" #include "GenEnum.h" #include "GenIntegerLiteral.h" #include "GenType.h" @@ -1190,6 +1191,7 @@ void IRGenModule::emitAutolinkInfo() { var->setSection(".swift1_autolink_entries"); var->setAlignment(getPointerAlignment().getValue()); + disableAddressSanitizer(*this, var); addUsedGlobal(var); } @@ -1329,6 +1331,15 @@ void IRGenModule::error(SourceLoc loc, const Twine &message) { bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); } +bool IRGenModule::shouldPrespecializeGenericMetadata() { + auto &context = getSwiftModule()->getASTContext(); + auto deploymentAvailability = + AvailabilityContext::forDeploymentTarget(context); + return IRGen.Opts.PrespecializeGenericMetadata && + deploymentAvailability.isContainedIn( + context.getPrespecializedGenericMetadataAvailability()); +} + void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) { assert(GenModules.count(SF) == 0); GenModules[SF] = IGM; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 1df95e74f923e..7f7e4a7889698 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -244,6 +244,21 @@ class IRGenerator { /// queued up. llvm::SmallPtrSet LazilyEmittedFieldMetadata; + /// Maps every generic type that is specialized within the module to its + /// specializations. + llvm::DenseMap> + SpecializationsForGenericTypes; + + /// The queue of specialized generic types whose prespecialized metadata to + /// emit. + llvm::SmallVector LazySpecializedTypeMetadataRecords; + + /// The queue of metadata accessors to emit. + /// + /// The accessors must be emitted after everything else which might result in + /// a statically-known-canonical prespecialization. + llvm::SmallSetVector LazyMetadataAccessors; + struct LazyOpaqueInfo { bool IsDescriptorUsed = false; bool IsDescriptorEmitted = false; @@ -331,7 +346,7 @@ class IRGenerator { /// Emit functions, variables and tables which are needed anyway, e.g. because /// they are externally visible. - void emitGlobalTopLevel(); + void emitGlobalTopLevel(llvm::StringSet<> *LinkerDirectives); /// Emit references to each of the protocol descriptors defined in this /// IR module. @@ -375,10 +390,22 @@ class IRGenerator { void ensureRelativeSymbolCollocation(SILDefaultWitnessTable &wt); + llvm::SmallVector specializationsForType(NominalTypeDecl *type) { + return SpecializationsForGenericTypes.lookup(type); + } + + void noteUseOfMetadataAccessor(NominalTypeDecl *decl) { + if (LazyMetadataAccessors.count(decl) == 0) { + LazyMetadataAccessors.insert(decl); + } + } + void noteUseOfTypeMetadata(NominalTypeDecl *type) { noteUseOfTypeGlobals(type, true, RequireMetadata); } + void noteUseOfSpecializedGenericTypeMetadata(CanType type); + void noteUseOfTypeMetadata(CanType type) { type.visit([&](Type t) { if (auto *nominal = t->getAnyNominal()) @@ -510,6 +537,7 @@ class IRGenModule { ModuleDecl *ClangImporterModule = nullptr; SourceFile *CurSourceFile = nullptr; + llvm::StringMap OriginalModules; llvm::SmallString<128> OutputFilename; llvm::SmallString<128> MainInputFilenameForDebugInfo; @@ -743,6 +771,8 @@ class IRGenModule { void error(SourceLoc loc, const Twine &message); bool useDllStorage(); + + bool shouldPrespecializeGenericMetadata(); Size getAtomicBoolSize() const { return AtomicBoolSize; } Alignment getAtomicBoolAlignment() const { return AtomicBoolAlign; } @@ -1122,7 +1152,8 @@ class IRGenModule { CanGenericSignature genericSig); /// Produce an associated type witness that refers to the given type. - llvm::Constant *getAssociatedTypeWitness(Type type, bool inProtocolContext); + llvm::Constant *getAssociatedTypeWitness(Type type, GenericSignature sig, + bool inProtocolContext); void emitAssociatedTypeMetadataRecord(const RootProtocolConformance *C); void emitFieldDescriptor(const NominalTypeDecl *Decl); @@ -1281,6 +1312,9 @@ private: \ ForDefinition_t forDefinition); llvm::Constant *getAddrOfValueWitnessTable(CanType concreteType, ConstantInit init = ConstantInit()); + llvm::Constant * + getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init = ConstantInit()); Optional getAddrOfIVarInitDestroy(ClassDecl *cd, bool isDestroyer, bool isForeign, @@ -1340,6 +1374,7 @@ private: \ ConstantInit definition = ConstantInit()); llvm::Constant *getAddrOfObjCModuleContextDescriptor(); llvm::Constant *getAddrOfClangImporterModuleContextDescriptor(); + llvm::Constant *getAddrOfOriginalModuleContextDescriptor(StringRef Name); ConstantReference getAddrOfParentContextDescriptor(DeclContext *from, bool fromAnonymousContext); ConstantReference getAddrOfContextDescriptorForParent(DeclContext *parent, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 2eea84a734e08..d3394346328bd 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -389,10 +389,6 @@ class IRGenSILFunction : /// Keeps track of the mapping of source variables to -O0 shadow copy allocas. llvm::SmallDenseMap ShadowStackSlots; llvm::SmallDenseMap, 8> AnonymousVariables; - /// To avoid inserting elements into ValueDomPoints twice. - llvm::SmallDenseSet ValueVariables; - /// Holds the DominancePoint of values that are storage for a source variable. - SmallVector, 8> ValueDomPoints; unsigned NumAnonVars = 0; /// Accumulative amount of allocated bytes on the stack. Used to limit the @@ -661,78 +657,6 @@ class IRGenSILFunction : return Name; } - /// Try to emit an inline assembly gadget which extends the lifetime of - /// \p Var. Returns whether or not this was successful. - bool emitLifetimeExtendingUse(llvm::Value *Var) { - llvm::Type *ArgTys; - auto *Ty = Var->getType(); - // Vectors, Pointers and Floats are expected to fit into a register. - if (Ty->isPointerTy() || Ty->isFloatingPointTy() || Ty->isVectorTy()) - ArgTys = {Ty}; - else { - // If this is not a scalar or vector type, we can't handle it. - if (isa(Ty)) - return false; - // The storage is guaranteed to be no larger than the register width. - // Extend the storage so it would fit into a register. - llvm::Type *IntTy; - switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) { - case 64: - IntTy = IGM.Int64Ty; - break; - case 32: - IntTy = IGM.Int32Ty; - break; - default: - llvm_unreachable("unsupported register width"); - } - ArgTys = {IntTy}; - Var = Builder.CreateZExtOrBitCast(Var, IntTy); - } - // Emit an empty inline assembler expression depending on the register. - auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false); - auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true); - Builder.CreateAsmCall(InlineAsm, Var); - return true; - } - - /// At -Onone, forcibly keep all LLVM values that are tracked by - /// debug variables alive by inserting an empty inline assembler - /// expression depending on the value in the blocks dominated by the - /// value. - void emitDebugVariableRangeExtension(const SILBasicBlock *CurBB) { - if (IGM.IRGen.Opts.shouldOptimize()) - return; - for (auto &Variable : ValueDomPoints) { - llvm::Instruction *Var = Variable.first; - DominancePoint VarDominancePoint = Variable.second; - if (getActiveDominancePoint() == VarDominancePoint || - isActiveDominancePointDominatedBy(VarDominancePoint)) { - bool ExtendedLifetime = emitLifetimeExtendingUse(Var); - if (!ExtendedLifetime) - continue; - - // Propagate dbg.values for Var into the current basic block. Note - // that this shouldn't be necessary. LiveDebugValues should be doing - // this but can't in general because it currently only tracks register - // locations. - llvm::BasicBlock *BB = Var->getParent(); - llvm::BasicBlock *CurBB = Builder.GetInsertBlock(); - if (BB == CurBB) - // The current basic block must be a successor of the dbg.value(). - continue; - - llvm::SmallVector DbgValues; - llvm::findDbgValues(DbgValues, Var); - for (auto *DVI : DbgValues) - if (DVI->getParent() == BB) - IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic( - DVI->getValue(), DVI->getVariable(), DVI->getExpression(), - DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt()); - } - } - } - /// To make it unambiguous whether a `var` binding has been initialized, /// zero-initialize the shadow copy alloca. LLDB uses the first pointer-sized /// field to recognize to detect uninitizialized variables. This can be @@ -757,6 +681,9 @@ class IRGenSILFunction : /// Account for bugs in LLVM. /// + /// - When a variable is spilled into a stack slot, LiveDebugValues fails to + /// recognize a restore of that slot for a different variable. + /// /// - The LLVM type legalizer currently doesn't update debug /// intrinsics when a large value is split up into smaller /// pieces. Note that this heuristic as a bit too conservative @@ -764,9 +691,7 @@ class IRGenSILFunction : /// /// - CodeGen Prepare may drop dbg.values pointing to PHI instruction. bool needsShadowCopy(llvm::Value *Storage) { - return (IGM.DataLayout.getTypeSizeInBits(Storage->getType()) > - IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) || - isa(Storage); + return !isa(Storage); } /// Unconditionally emit a stack shadow copy of an \c llvm::Value. @@ -800,27 +725,10 @@ class IRGenSILFunction : if (IGM.IRGen.Opts.DisableDebuggerShadowCopies || IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || isa(Storage) || isa(Storage) || - Storage->getType() == IGM.RefCountedPtrTy) + Storage->getType() == IGM.RefCountedPtrTy || !needsShadowCopy(Storage)) return Storage; - // Always emit shadow copies for function arguments. - if (VarInfo.ArgNo == 0) - // Otherwise only if debug value range extension is not feasible. - if (!needsShadowCopy(Storage)) { - // Mark for debug value range extension unless this is a constant, or - // unless it's not possible to emit lifetime-extending uses for this. - if (auto *Value = dyn_cast(Storage)) { - // Emit a use at the start of the storage lifetime to force early - // materialization. This makes variables available for inspection as - // soon as they are defined. - bool ExtendedLifetime = emitLifetimeExtendingUse(Value); - if (ExtendedLifetime) - if (ValueVariables.insert(Value).second) - ValueDomPoints.push_back({Value, getActiveDominancePoint()}); - } - - return Storage; - } + // Emit a shadow copy. return emitShadowCopy(Storage, Scope, VarInfo, Align); } @@ -1900,9 +1808,6 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { IGM.DebugInfo->setCurrentLoc( Builder, DS, RegularLocation::getAutoGeneratedLocation()); } - - if (isa(&I)) - emitDebugVariableRangeExtension(BB); } visit(&I); } @@ -4438,7 +4343,8 @@ void IRGenSILFunction::visitBeginUnpairedAccessInst( // in which case we should use the caller, which is generally ok because // materializeForSet can't usually be thunked. llvm::Value *pc; - if (hasBeenInlined(access)) { + // hack: wasm doesn't have returnaddress + if (true || hasBeenInlined(access)) { pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } else { auto retAddrFn = diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index 795529c5f8a41..8b54e77503875 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -314,18 +314,20 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl) super::noteStartOfGenericRequirements(forClass); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericWitnessTable(forClass); + super::addGenericWitnessTable(requirement, forClass); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericArgument(forClass); + super::addGenericArgument(requirement, forClass); } void addMethod(SILDeclRef fn) { diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 57d708745c648..f075bcc2ada55 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -19,24 +19,31 @@ #include "ConstantBuilder.h" #include "Explosion.h" #include "FixedTypeInfo.h" -#include "GenericRequirement.h" #include "GenArchetype.h" #include "GenClass.h" #include "GenMeta.h" #include "GenProto.h" #include "GenType.h" +#include "GenericArguments.h" +#include "GenericRequirement.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenMangler.h" #include "IRGenModule.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/SubstitutionMap.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/TypeLowering.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Constant.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" +#include using namespace swift; using namespace irgen; @@ -432,60 +439,6 @@ static llvm::Value *emitObjCMetadataRef(IRGenFunction &IGF, return emitObjCMetadataRefForMetadata(IGF, classPtr); } -namespace { - /// A structure for collecting generic arguments for emitting a - /// nominal metadata reference. The structure produced here is - /// consumed by swift_getGenericMetadata() and must correspond to - /// the fill operations that the compiler emits for the bound decl. - struct GenericArguments { - /// The values to use to initialize the arguments structure. - SmallVector Values; - SmallVector Types; - - static unsigned getNumGenericArguments(IRGenModule &IGM, - NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - return requirements.getNumTypeRequirements(); - } - - void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - collectTypes(IGM, requirements); - } - - void collectTypes(IRGenModule &IGM, - const GenericTypeRequirements &requirements) { - for (auto &requirement : requirements.getRequirements()) { - if (requirement.Protocol) { - Types.push_back(IGM.WitnessTablePtrTy); - } else { - Types.push_back(IGM.TypeMetadataPtrTy); - } - } - } - - void collect(IRGenFunction &IGF, CanType type) { - auto *decl = type.getNominalOrBoundGenericNominal(); - GenericTypeRequirements requirements(IGF.IGM, decl); - - auto subs = - type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); - requirements.enumerateFulfillments( - IGF.IGM, subs, - [&](unsigned reqtIndex, CanType type, ProtocolConformanceRef conf) { - if (conf) { - Values.push_back(emitWitnessTableRef(IGF, type, conf)); - } else { - Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); - } - }); - - collectTypes(IGF.IGM, decl); - assert(Types.size() == Values.size()); - } - }; -} // end anonymous namespace - static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) { // ObjC classes are type erased. // TODO: Unless they have magic methods... @@ -638,6 +591,22 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF, classObject); } +static MetadataResponse emitNominalPrespecializedGenericMetadataRef( + IRGenFunction &IGF, NominalTypeDecl *theDecl, CanType theType, + DynamicMetadataRequest request) { + assert(isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)); + // We are applying generic parameters to a generic type. + assert(theType->getAnyNominal() == theDecl); + + // Check to see if we've maybe got a local reference already. + if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request)) + return cache; + + auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType); + return MetadataResponse::forComplete(metadata); +} + /// Returns a metadata reference for a nominal type. /// /// This is only valid in a couple of special cases: @@ -678,6 +647,12 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, theDecl->getGenericSignature()->areAllParamsConcrete()) && "no generic args?!"); + if (isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)) { + return emitNominalPrespecializedGenericMetadataRef(IGF, theDecl, theType, + request); + } + // Call the generic metadata accessor function. llvm::Function *accessor = IGF.IGM.getAddrOfGenericTypeMetadataAccessFunction(theDecl, @@ -692,6 +667,72 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, return response; } +bool irgen::isNominalGenericContextTypeMetadataAccessTrivial( + IRGenModule &IGM, NominalTypeDecl &nominal, CanType type) { + assert(nominal.isGenericContext()); + + if (!IGM.shouldPrespecializeGenericMetadata()) { + return false; + } + + if (type->hasArchetype()) { + return false; + } + + if (nominal.getModuleContext() != IGM.getSwiftModule() || + nominal.isResilient(IGM.getSwiftModule(), ResilienceExpansion::Minimal)) { + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support enums. + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support classes. + return false; + } + + auto *generic = type.getAnyGeneric(); + assert(generic); + auto *environment = generic->getGenericEnvironment(); + assert(environment); + auto substitutions = + type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal); + + return llvm::all_of(environment->getGenericParams(), [&](auto parameter) { + auto conformances = + environment->getGenericSignature()->getConformsTo(parameter); + auto witnessTablesAreReferenceable = + llvm::all_of(conformances, [&](ProtocolDecl *conformance) { + return conformance->getModuleContext() == IGM.getSwiftModule() && + !conformance->isResilient(IGM.getSwiftModule(), + ResilienceExpansion::Minimal); + }); + auto argument = ((Type *)parameter)->subst(substitutions); + auto genericArgument = argument->getAnyGeneric(); + // For now, to avoid statically specializing generic protocol witness + // tables, don't statically specialize metadata for types any of whose + // arguments are generic. + // + // TODO: This is more pessimistic than necessary. Specialize even in + // the face of generic arguments so long as those arguments + // aren't required to conform to any protocols. + // + // TODO: Once witness tables are statically specialized, check whether the + // ConformanceInfo returns nullptr from tryGetConstantTable. + // early return. + auto isGeneric = genericArgument && genericArgument->isGenericContext(); + auto isNominal = argument->getNominalOrBoundGenericNominal(); + auto isExistential = argument->isExistentialType(); + return isNominal && !isGeneric && !isExistential && + witnessTablesAreReferenceable && + irgen::isTypeMetadataAccessTrivial(IGM, + argument->getCanonicalType()); + }) && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); +} + /// Is it basically trivial to access the given metadata? If so, we don't /// need a cache variable in its accessor. bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { @@ -707,14 +748,14 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (isa(nominalDecl->getModuleScopeContext())) return false; - // Generic type metadata always requires an accessor. if (nominalDecl->isGenericContext()) - return false; + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); auto expansion = ResilienceExpansion::Maximal; // Resiliently-sized metadata access always requires an accessor. - return (IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion)); + return IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion); } // The empty tuple type has a singleton metadata. @@ -739,6 +780,18 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (type->hasDynamicSelfType()) return true; + if (isa(type) || isa(type)) { + auto nominalType = cast(type); + auto *nominalDecl = nominalType->getDecl(); + + // Imported type metadata always requires an accessor. + if (isa(nominalDecl->getModuleScopeContext())) + return false; + + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); + } + return false; } @@ -1683,11 +1736,77 @@ IRGenFunction::emitGenericTypeMetadataAccessFunctionCall( return MetadataResponse::handle(*this, request, call); } -static MetadataResponse -emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, - Explosion ¶ms, - NominalTypeDecl *nominal, - GenericArguments &genericArgs) { +static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs, + std::function valueAtIndex) { + auto &IGM = IGF.IGM; + auto specializations = IGF.IGM.IRGen.specializationsForType(nominal); + if (specializations.size() > 0) { + SmallVector conditionBlocks; + for (size_t index = 0; index < specializations.size(); ++index) { + conditionBlocks.push_back(llvm::BasicBlock::Create(IGM.getLLVMContext())); + } + + IGF.Builder.CreateBr(conditionBlocks[0]); + + SmallVector, 4> + specializationBlocks; + auto switchDestination = llvm::BasicBlock::Create(IGM.getLLVMContext()); + unsigned long index = 0; + for (auto specialization : specializations) { + auto conditionBlock = conditionBlocks[index]; + IGF.Builder.emitBlock(conditionBlock); + auto successorBlock = index < conditionBlocks.size() - 1 + ? conditionBlocks[index + 1] + : switchDestination; + auto specializationBlock = llvm::BasicBlock::Create(IGM.getLLVMContext()); + auto substitutions = specialization->getContextSubstitutionMap( + IGM.getSwiftModule(), nominal); + + llvm::Value *condition = llvm::ConstantInt::get(IGM.Int1Ty, 1); + auto generic = specialization->getAnyGeneric(); + auto parameters = generic->getGenericEnvironment()->getGenericParams(); + for (size_t index = 0; index < parameters.size(); ++index) { + auto parameter = parameters[index]; + auto argument = ((Type *)parameter)->subst(substitutions); + llvm::Constant *addr = + IGM.getAddrOfTypeMetadata(argument->getCanonicalType()); + auto addrInt = IGF.Builder.CreateBitCast(addr, IGM.Int8PtrTy); + condition = IGF.Builder.CreateAnd( + condition, IGF.Builder.CreateICmpEQ(addrInt, valueAtIndex(index))); + } + IGF.Builder.CreateCondBr(condition, specializationBlock, successorBlock); + + auto specializedMetadataAddress = + IGM.getAddrOfTypeMetadata(specialization); + // Construct a MetadataResponse. It has three fields in the following + // order: + // - const Metadata *Metadata; + // - MetadataState (i32) StaticState; + llvm::Value *response = llvm::UndefValue::get(IGM.TypeMetadataResponseTy); + response = IGF.Builder.CreateInsertValue( + response, specializedMetadataAddress, 0, + "insert metadata address into response"); + auto state = + llvm::ConstantInt::get(IGM.SizeTy, (uint32_t)MetadataState::Complete); + response = IGF.Builder.CreateInsertValue( + response, state, 1, "insert metadata state into response"); + specializationBlocks.push_back({specializationBlock, response}); + ++index; + } + + for (auto pair : specializationBlocks) { + IGF.Builder.emitBlock(pair.first); + IGF.Builder.CreateRet(pair.second); + } + IGF.Builder.emitBlock(switchDestination); + } +} + +MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs) { auto &IGM = IGF.IGM; llvm::Constant *descriptor = @@ -1706,6 +1825,21 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, llvm::Value *arguments = IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrTy); + llvm::Value *argumentsBuffer = + IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrPtrTy); + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, [&](int index) { + llvm::Value *indexValue = llvm::ConstantInt::get(IGM.Int64Ty, index); + llvm::SmallVector indices{indexValue}; + llvm::Value *elementPointer = + IGF.Builder.CreateGEP(argumentsBuffer, indexValue); + llvm::LoadInst *retval = IGF.Builder.CreateLoad( + elementPointer, Alignment(), + llvm::formatv("load argument at index {0} from buffer", index)); + return retval; + }); + // Make the call. auto call = IGF.Builder.CreateCall(IGM.getGetGenericMetadataFn(), {request, arguments, descriptor}); @@ -1791,7 +1925,13 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, auto arg2 = numArguments >= 3 ? IGF.Builder.CreateBitCast(params.claimNext(), IGM.Int8PtrTy) : llvm::UndefValue::get(IGM.Int8PtrTy); - + + std::array argValues{arg0, arg1, arg2}; + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, + [&](int index) { return argValues[index]; }); + auto call = IGF.Builder.CreateCall(thunkFn, {request, arg0, arg1, arg2, descriptor}); call->setDoesNotAccessMemory(); @@ -1986,18 +2126,7 @@ irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM, if (!shouldDefine || !accessor->empty()) return accessor; - if (IGM.getOptions().optimizeForSize()) - accessor->addFnAttr(llvm::Attribute::NoInline); - - bool isReadNone = - (genericArgs.Types.size() <= NumDirectGenericTypeMetadataAccessFunctionArgs); - - emitCacheAccessFunction(IGM, accessor, /*cache*/nullptr, CacheStrategy::None, - [&](IRGenFunction &IGF, Explosion ¶ms) { - return emitGenericTypeMetadataAccessFunction( - IGF, params, nominal, genericArgs); - }, - isReadNone); + IGM.IRGen.noteUseOfMetadataAccessor(nominal); return accessor; } @@ -2288,14 +2417,19 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, IGM.Int32Ty); stringAddrOffset = subIGF.Builder.CreateSExtOrBitCast(stringAddrOffset, IGM.SizeTy); - auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); - if (IGM.getModule()->getDataLayout().isBigEndian()) { - stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, - llvm::ConstantInt::get(IGM.SizeTy, 4)); + llvm::Value *stringAddr; + if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + stringAddr = subIGF.Builder.CreateIntToPtr(stringAddrOffset, IGM.Int8PtrTy); + } else { + auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); + if (IGM.getModule()->getDataLayout().isBigEndian()) { + stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, + llvm::ConstantInt::get(IGM.SizeTy, 4)); + } + stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, + stringAddrOffset); + stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); } - auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, - stringAddrOffset); - stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); llvm::CallInst *call; if (request.isStaticallyAbstract()) { diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index d9a0471d8e14b..4c39a03e8199d 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -34,6 +34,7 @@ enum ForDefinition_t : bool; namespace irgen { class ConstantReference; class Explosion; +struct GenericArguments; class IRGenFunction; class IRGenModule; class MetadataDependencyCollector; @@ -503,6 +504,10 @@ static inline bool isAccessorLazilyGenerated(MetadataAccessStrategy strategy) { /// need a cache variable in its accessor. bool isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type); +bool isNominalGenericContextTypeMetadataAccessTrivial(IRGenModule &IGM, + NominalTypeDecl &nominal, + CanType type); + /// Determine how the given type metadata should be accessed. MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type); @@ -583,6 +588,10 @@ void emitCacheAccessFunction(IRGenModule &IGM, CacheStrategy cacheStrategy, CacheEmitter getValue, bool isReadNone = true); +MetadataResponse +emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, Explosion ¶ms, + NominalTypeDecl *nominal, + GenericArguments &genericArgs); /// Emit a declaration reference to a metatype object. void emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type, diff --git a/lib/IRGen/NominalMetadataVisitor.h b/lib/IRGen/NominalMetadataVisitor.h index 6ac633abf6d31..212d7c65640c9 100644 --- a/lib/IRGen/NominalMetadataVisitor.h +++ b/lib/IRGen/NominalMetadataVisitor.h @@ -53,9 +53,9 @@ template class NominalMetadataVisitor GenericTypeRequirements requirements(super::IGM, typeDecl); for (auto reqt : requirements.getRequirements()) { if (reqt.Protocol) { - asImpl().addGenericWitnessTable(args...); + asImpl().addGenericWitnessTable(reqt, args...); } else { - asImpl().addGenericArgument(args...); + asImpl().addGenericArgument(reqt, args...); } } diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 837ea00e5087e..147d2672bb162 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -18,6 +18,7 @@ #define SWIFT_IRGEN_STRUCTMETADATALAYOUT_H #include "NominalMetadataVisitor.h" +#include "swift/AST/IRGenOptions.h" namespace swift { namespace irgen { @@ -61,6 +62,9 @@ template class StructMetadataVisitor asImpl().addFieldOffset(prop); asImpl().noteEndOfFieldOffsets(); + + if (asImpl().hasTrailingFlags()) + asImpl().addTrailingFlags(); } // Note the start of the field offset vector. @@ -68,6 +72,11 @@ template class StructMetadataVisitor // Note the end of the field offset vector. void noteEndOfFieldOffsets() {} + + bool hasTrailingFlags() { + return Target->isGenericContext() && + IGM.shouldPrespecializeGenericMetadata(); + } }; /// An "implementation" of StructMetadataVisitor that just scans through @@ -87,14 +96,16 @@ class StructMetadataScanner : public StructMetadataVisitor { void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } void addFieldOffset(VarDecl *) { addInt32(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void noteStartOfTypeSpecificMembers() {} void noteEndOfFieldOffsets() { NextOffset = NextOffset.roundUpToAlignment(super::IGM.getPointerAlignment()); } + void addTrailingFlags() { addPointer(); } + private: void addPointer() { NextOffset += super::IGM.getPointerSize(); diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index c9e27358976d7..01bbe6ae504f3 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -12,7 +12,9 @@ target_link_libraries(swiftImmediate PRIVATE swiftIRGen swiftSILGen swiftSILOptimizer) - -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swiftImmediate PRIVATE edit) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) + target_compile_definitions(swiftImmediate PRIVATE + HAVE_LIBEDIT) + target_link_libraries(swiftImmediate PRIVATE + ${LibEdit_LIBRARIES}) endif() diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 4d8362d762138..67c26f1c7d2d3 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -37,7 +37,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT #include #include #endif @@ -120,8 +120,8 @@ class ConvertForWcharSize<4> { }; using Convert = ConvertForWcharSize; - -#if HAVE_UNICODE_LIBEDIT + +#if HAVE_LIBEDIT static void convertFromUTF8(llvm::StringRef utf8, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + utf8.size(); @@ -135,7 +135,7 @@ static void convertFromUTF8(llvm::StringRef utf8, (void)res; out.set_size(wide_begin - out.begin()); } - + static void convertToUTF8(llvm::ArrayRef wide, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + wide.size()*4; @@ -153,7 +153,7 @@ static void convertToUTF8(llvm::ArrayRef wide, } // end anonymous namespace -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT static ModuleDecl * typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, @@ -187,12 +187,10 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, REPLInputFile.addImports(ImportsWithOptions); } - bool FoundAnySideEffects = false; bool Done; do { - FoundAnySideEffects |= - parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, - &PersistentState); + parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, + &PersistentState); } while (!Done); performTypeChecking(REPLInputFile); return REPLModule; diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index ca90d075fde2e..ad7b9db5f296f 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -362,10 +362,10 @@ class IndexSwiftASTWalker : public SourceEntityWalker { } void handleMemberwiseInitRefs(Expr *E) { - if (!isa(E)) + if (!isa(E)) return; - auto *DeclRef = dyn_cast(cast(E)->getFn()); + auto *DeclRef = dyn_cast(cast(E)->getFn()); if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl())) return; diff --git a/lib/Markup/LineList.cpp b/lib/Markup/LineList.cpp index 0afeb5eec995d..a89488b8c4213 100644 --- a/lib/Markup/LineList.cpp +++ b/lib/Markup/LineList.cpp @@ -115,8 +115,6 @@ LineList MarkupContext::getLineList(swift::RawComment RC) { // Determine if we have leading decorations in this block comment. bool HasASCIIArt = false; if (swift::startsWithNewline(Cleaned)) { - Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(), - C.Range.getStart() }); unsigned NewlineBytes = swift::measureNewline(Cleaned); Cleaned = Cleaned.drop_front(NewlineBytes); CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes); diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 38535f82610fc..0cc1870cbd9ff 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -231,21 +231,6 @@ class ChildIndexFinder : public TypeReprVisitor { } }; -static ValueDecl* getReferencedDecl(Expr *E) { - // Get the syntactic expression out of an implicit expression. - if (auto *ICE = dyn_cast(E)) - E = ICE->getSyntacticSubExpr(); - if (auto *DRE = dyn_cast(E)) { - return DRE->getDecl(); - } else if (auto *MRE = dyn_cast(E)) { - return MRE->getMember().getDecl(); - } else if (auto OtherCtorE = dyn_cast(E)) { - return OtherCtorE->getDecl(); - } else { - return nullptr; - } -} - struct ConversionFunctionInfo { Expr *ExpressionToWrap; SmallString<256> Buffer; @@ -582,15 +567,10 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } return false; }; - if (auto *DSC = dyn_cast(Call)) { - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - if (handleDecl(FD, Call->getSourceRange())) - return true; - } - } else if (auto MRE = dyn_cast(Call)) { - if (handleDecl(MRE->getReferencedDecl().getDecl(), MRE->getSourceRange())) + if (auto *VD = getReferencedDecl(Call).second.getDecl()) + if (handleDecl(VD, Call->getSourceRange())) return true; - } + return false; } @@ -858,11 +838,12 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Lexer::getLocForEndOfToken(SM, E->getEndLoc())), Text); } - bool wrapAttributeReference(Expr* Reference, Expr* WrapperTarget, + bool wrapAttributeReference(Expr *Reference, Expr *WrapperTarget, bool FromString) { - auto *RD = getReferencedDecl(Reference); + auto *RD = Reference->getReferencedDecl().getDecl(); if (!RD) return false; + std::string Rename; Optional Kind; StringRef LeftComment; @@ -1110,7 +1091,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { // reference of the property. bool handlePropertyTypeChange(Expr *E) { if (auto MRE = dyn_cast(E)) { - if (auto *VD = MRE->getReferencedDecl().getDecl()) { + if (auto *VD = MRE->getMember().getDecl()) { for (auto *I: getRelatedDiffItems(VD)) { if (auto *Item = dyn_cast(I)) { if (Item->DiffKind == NodeAnnotation::WrapOptional && @@ -1143,46 +1124,36 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { if (auto *CE = dyn_cast(E)) { auto Fn = CE->getFn(); auto Args = CE->getArg(); - switch (Fn->getKind()) { - case ExprKind::DeclRef: { - if (auto FD = Fn->getReferencedDecl().getDecl()) { - handleFuncRename(FD, Fn, Args); - handleTypeHoist(FD, CE, Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); - } - break; - } - case ExprKind::DotSyntaxCall: { - auto DSC = cast(Fn); - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, DSC->getFn(), Args); - handleFunctionCallToPropertyChange(FD, DSC->getFn(), Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *DRE = dyn_cast(Fn)) { + if (auto *VD = DRE->getDecl()) { + if (VD->getNumCurryLevels() == 1) { + handleFuncRename(VD, Fn, Args); + handleTypeHoist(VD, CE, Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; } - case ExprKind::ConstructorRefCall: { - auto CCE = cast(Fn); - if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *SelfApply = dyn_cast(Fn)) { + if (auto VD = SelfApply->getFn()->getReferencedDecl().getDecl()) { + if (VD->getNumCurryLevels() == 2) { + handleFuncRename(VD, SelfApply->getFn(), Args); + handleFunctionCallToPropertyChange(VD, SelfApply->getFn(), Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; - } - default: - break; } } return true; } - static void collectParamters(AbstractFunctionDecl *AFD, - SmallVectorImpl &Results) { + static void collectParameters(AbstractFunctionDecl *AFD, + SmallVectorImpl &Results) { for (auto PD : *AFD->getParameters()) { Results.push_back(PD); } @@ -1197,7 +1168,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor.replace(NameRange, View.base()); unsigned Index = 0; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); for (auto *PD: Params) { if (Index == View.argSize()) break; @@ -1322,7 +1293,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { return; Idx --; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); if (Params.size() <= Idx) return; @@ -1397,11 +1368,11 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor(Editor), USRs(USRs) {} bool isSuperExpr(Expr *E) { if (E->isImplicit()) - return false; + return false; // Check if the expression is super.foo(). if (auto *CE = dyn_cast(E)) { if (auto *DSC = dyn_cast(CE->getFn())) { - if (DSC->getBase()->getKind() != ExprKind::SuperRef) + if (!isa(DSC->getBase())) return false; llvm::SmallString<64> Buffer; llvm::raw_svector_ostream OS(Buffer); @@ -1419,9 +1390,9 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } std::pair walkToStmtPre(Stmt *S) override { if (auto *BS = dyn_cast(S)) { - for(auto Ele: BS->getElements()) { - if (Ele.is() && isSuperExpr(Ele.get())) { - Editor.remove(Ele.getSourceRange()); + for(auto Ele: BS->getElements()) { + if (Ele.is() && isSuperExpr(Ele.get())) { + Editor.remove(Ele.getSourceRange()); } } } diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 0c7a51f7e7ff4..7fd7e5915725d 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -2482,7 +2482,8 @@ void Lexer::lexImpl() { } } -Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { +Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM) { // Don't try to do anything with an invalid location. if (!Loc.isValid()) return Token(); @@ -2501,7 +2502,7 @@ Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { // (making this option irrelevant), or the caller lexed comments and // we need to lex just the comment token. Lexer L(FakeLangOpts, SM, BufferID, nullptr, LexerMode::Swift, - HashbangMode::Allowed, CommentRetentionMode::ReturnAsTokens); + HashbangMode::Allowed, CRM); L.restoreState(State(Loc)); return L.peekNextToken(); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 9d4ededb03921..95f3052810952 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -175,7 +175,7 @@ namespace { DebuggerClient *debug_client = getDebuggerClient(); assert (debug_client); debug_client->didGlobalize(D); - SF->Decls.push_back(D); + SF->addTopLevelDecl(D); P.markWasHandled(D); } }; @@ -189,7 +189,7 @@ namespace { /// decl-sil [[only in SIL mode] /// decl-sil-stage [[only in SIL mode] /// \endverbatim -bool Parser::parseTopLevel() { +void Parser::parseTopLevel() { SF.ASTStage = SourceFile::Parsing; // Prime the lexer. @@ -246,22 +246,11 @@ bool Parser::parseTopLevel() { consumeToken(); } - // If this is a Main source file, determine if we found code that needs to be - // executed (this is used by the repl to know whether to compile and run the - // newly parsed stuff). - bool FoundTopLevelCodeToExecute = false; - if (allowTopLevelCode()) { - for (auto V : Items) { - if (isa(V.get())) - FoundTopLevelCodeToExecute = true; - } - } - // Add newly parsed decls to the module. for (auto Item : Items) { if (auto *D = Item.dyn_cast()) { assert(!isa(D) && "accessors should not be added here"); - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); } } @@ -279,8 +268,6 @@ bool Parser::parseTopLevel() { SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia); TokReceiver->finalize(); } - - return FoundTopLevelCodeToExecute; } ParserResult Parser::parseExtendedAvailabilitySpecList( @@ -768,11 +755,10 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { } if (!Status.shouldStopParsing()) { - MemberName = - parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, - diag::attr_implements_expected_member_name, - /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); + MemberName = parseDeclNameRef(MemberNameLoc, + diag::attr_implements_expected_member_name, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowOperators); if (!MemberName) { Status.setIsParseError(); } @@ -806,7 +792,7 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { /// /// \verbatim /// differentiable-attribute-arguments: -/// '(' (differentiation-params-clause ',')? +/// '(' (differentiability-params-clause ',')? /// (differentiable-attr-func-specifier ',')? /// differentiable-attr-func-specifier? /// where-clause? @@ -819,7 +805,7 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "differentiable"; SourceLoc lParenLoc = loc, rParenLoc = loc; bool linear = false; - SmallVector params; + SmallVector parameters; Optional jvpSpec; Optional vjpSpec; TrailingWhereClause *whereClause = nullptr; @@ -827,8 +813,8 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { // Parse '('. if (consumeIf(tok::l_paren, lParenLoc)) { // Parse @differentiable attribute arguments. - if (parseDifferentiableAttributeArguments(linear, params, jvpSpec, vjpSpec, - whereClause)) + if (parseDifferentiableAttributeArguments(linear, parameters, jvpSpec, + vjpSpec, whereClause)) return makeParserError(); // Parse ')'. if (!consumeIf(tok::r_paren, rParenLoc)) { @@ -838,10 +824,9 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { } } - return ParserResult( - DifferentiableAttr::create(Context, /*implicit*/ false, atLoc, - SourceRange(loc, rParenLoc), linear, - params, jvpSpec, vjpSpec, whereClause)); + return ParserResult(DifferentiableAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), linear, + parameters, jvpSpec, vjpSpec, whereClause)); } // Attribute parsing error helper. @@ -861,61 +846,67 @@ static bool errorAndSkipUntilConsumeRightParen(Parser &P, StringRef attrName, return true; }; -/// Parse a differentiation parameters 'wrt:' clause, returning true on error. +/// Parse a differentiability parameters 'wrt:' clause, returning true on error. +/// If `allowNamedParameters` is false, allow only index parameters and 'self'. /// /// \verbatim -/// differentiation-params-clause: -/// 'wrt' ':' (differentiation-param | differentiation-params) -/// differentiation-params: -/// '(' differentiation-param (',' differentiation-param)* ')' -/// differentiation-param: -/// 'self' | identifier +/// differentiability-params-clause: +/// 'wrt' ':' (differentiability-param | differentiability-params) +/// differentiability-params: +/// '(' differentiability-param (',' differentiability-param)* ')' +/// differentiability-param: +/// 'self' | identifier | [0-9]+ /// \endverbatim -bool Parser::parseDifferentiationParametersClause( - SmallVectorImpl ¶ms, StringRef attrName) { +bool Parser::parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, + bool allowNamedParameters) { SyntaxParsingContext DiffParamsClauseContext( - SyntaxContext, SyntaxKind::DifferentiationParamsClause); + SyntaxContext, SyntaxKind::DifferentiationParamsClause); consumeToken(tok::identifier); if (!consumeIf(tok::colon)) { diagnose(Tok, diag::expected_colon_after_label, "wrt"); return errorAndSkipUntilConsumeRightParen(*this, attrName); } - // Function that parses a parameter into `params`. Returns true if error + // Function that parses a parameter into `parameters`. Returns true if error // occurred. auto parseParam = [&](bool parseTrailingComma = true) -> bool { SyntaxParsingContext DiffParamContext( SyntaxContext, SyntaxKind::DifferentiationParam); SourceLoc paramLoc; switch (Tok.getKind()) { - case tok::identifier: { - Identifier paramName; - if (parseIdentifier(paramName, paramLoc, - diag::diff_params_clause_expected_parameter)) - return true; - params.push_back(ParsedAutoDiffParameter::getNamedParameter( - paramLoc, paramName)); - break; - } - case tok::integer_literal: { - unsigned paramNum; - if (parseUnsignedInteger( - paramNum, paramLoc, - diag::diff_params_clause_expected_parameter)) - return true; - - params.push_back(ParsedAutoDiffParameter::getOrderedParameter( - paramLoc, paramNum)); - break; - } - case tok::kw_self: { - paramLoc = consumeToken(tok::kw_self); - params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); - break; + case tok::identifier: { + // If named parameters are not allowed, diagnose. + if (!allowNamedParameters) { + diagnose(Tok, diag::diff_params_clause_expected_parameter_unnamed); + return true; } - default: - diagnose(Tok, diag::diff_params_clause_expected_parameter); + Identifier paramName; + if (parseIdentifier(paramName, paramLoc, + diag::diff_params_clause_expected_parameter)) return true; + parameters.push_back( + ParsedAutoDiffParameter::getNamedParameter(paramLoc, paramName)); + break; + } + case tok::integer_literal: { + unsigned paramNum; + if (parseUnsignedInteger( + paramNum, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + parameters.push_back( + ParsedAutoDiffParameter::getOrderedParameter(paramLoc, paramNum)); + break; + } + case tok::kw_self: { + paramLoc = consumeToken(tok::kw_self); + parameters.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); + break; + } + default: + diagnose(Tok, diag::diff_params_clause_expected_parameter); + return true; } if (parseTrailingComma && Tok.isNot(tok::r_paren)) return parseToken(tok::comma, diag::attr_expected_comma, attrName, @@ -948,10 +939,9 @@ bool Parser::parseDifferentiationParametersClause( } bool Parser::parseDifferentiableAttributeArguments( - bool &linear, SmallVectorImpl ¶ms, + bool &linear, SmallVectorImpl ¶meters, Optional &jvpSpec, - Optional &vjpSpec, - TrailingWhereClause *&whereClause) { + Optional &vjpSpec, TrailingWhereClause *&whereClause) { StringRef AttrName = "differentiable"; // Parse trailing comma, if it exists, and check for errors. @@ -976,7 +966,7 @@ bool Parser::parseDifferentiableAttributeArguments( SyntaxParsingContext ContentContext( SyntaxContext, SyntaxKind::DifferentiableAttributeArguments); - // Parse optional differentiation parameters. + // Parse optional differentiability parameters. // Parse 'linear' label (optional). linear = false; if (isIdentifier(Tok, "linear")) { @@ -997,9 +987,9 @@ bool Parser::parseDifferentiableAttributeArguments( .fixItReplace(withRespectToRange, "wrt:"); return errorAndSkipUntilConsumeRightParen(*this, AttrName); } - // Parse differentiation parameters' clause. + // Parse the optional 'wrt' differentiability parameters clause. if (isIdentifier(Tok, "wrt")) { - if (parseDifferentiationParametersClause(params, AttrName)) + if (parseDifferentiabilityParametersClause(parameters, AttrName)) return true; // If no trailing comma or 'where' clause, terminate parsing arguments. if (Tok.isNot(tok::comma, tok::kw_where)) @@ -1013,8 +1003,8 @@ bool Parser::parseDifferentiableAttributeArguments( auto parseFuncSpec = [&](StringRef label, DeclNameRefWithLoc &result, bool &terminateParsingArgs) -> bool { // Parse label. - if (parseSpecificIdentifier(label, - diag::attr_differentiable_missing_label, label) || + if (parseSpecificIdentifier(label, diag::attr_missing_label, label, + AttrName) || parseToken(tok::colon, diag::expected_colon_after_label, label)) return true; // Parse the name of the function. @@ -1022,10 +1012,15 @@ bool Parser::parseDifferentiableAttributeArguments( SyntaxContext, SyntaxKind::FunctionDeclName); Diagnostic funcDiag(diag::attr_differentiable_expected_function_name.ID, { label }); - result.Name = - parseUnqualifiedDeclName(/*afterDot=*/false, result.Loc, - funcDiag, /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); + result.Name = parseDeclNameRef(result.Loc, funcDiag, + DeclNameFlag::AllowZeroArgCompoundNames | DeclNameFlag::AllowOperators); + // Emit warning for deprecated `jvp:` and `vjp:` arguments. + // TODO(TF-1001): Remove deprecated `jvp:` and `vjp:` arguments. + if (result.Loc.isValid()) { + diagnose(result.Loc.getStartLoc(), + diag::attr_differentiable_jvp_vjp_deprecated_warning) + .highlight(result.Loc.getSourceRange()); + } // If no trailing comma or 'where' clause, terminate parsing arguments. if (Tok.isNot(tok::comma, tok::kw_where)) terminateParsingArgs = true; @@ -1081,18 +1076,78 @@ bool Parser::parseDifferentiableAttributeArguments( return false; } +/// Helper function that parses 'type-identifier' for `parseQualifiedDeclName`. +/// Returns true on error. Sets `baseType` to the parsed base type if present, +/// or to `nullptr` if not. A missing base type is not considered an error. +static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { + baseType = nullptr; + + // If base type cannot be parsed, return false (no error). + if (!P.canParseBaseTypeForQualifiedDeclName()) + return false; + + auto result = P.parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true); + // If base type should be parseable but the actual base type result is null, + // return true (error). + if (result.isNull()) + return true; + + // Consume the leading period before the final declaration name component. + // `parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true)` leaves the + // leading period unparsed to avoid syntax verification errors. + assert(P.startsWithSymbol(P.Tok, '.') && "false"); + P.consumeStartingCharacterOfCurrentToken(tok::period); + + // Set base type and return false (no error). + baseType = result.getPtrOrNull(); + return false; +} + +/// Parses an optional base type, followed by a declaration name. +/// Returns true on error (if declaration name could not be parsed). +/// +/// \verbatim +/// qualified-decl-name: +/// type-identifier? unqualified-decl-name +/// type-identifier: +/// identifier generic-args? ('.' identifier generic-args?)* +/// \endverbatim +/// +// TODO(TF-1066): Use module qualified name syntax/parsing instead of custom +// qualified name syntax/parsing. +static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, + TypeRepr *&baseType, + DeclNameRefWithLoc &original) { + SyntaxParsingContext DeclNameContext(P.SyntaxContext, + SyntaxKind::QualifiedDeclName); + // Parse base type. + if (parseBaseTypeForQualifiedDeclName(P, baseType)) + return true; + // Parse final declaration name. + original.Name = P.parseDeclNameRef( + original.Loc, nameParseError, + Parser::DeclNameFlag::AllowZeroArgCompoundNames | + Parser::DeclNameFlag::AllowKeywordsUsingSpecialNames | + Parser::DeclNameFlag::AllowOperators); + // The base type is optional, but the final unqualified declaration name is + // not. If name could not be parsed, return true for error. + return !original.Name; +} + /// Parse a `@derivative(of:)` attribute, returning true on error. /// /// \verbatim /// derivative-attribute-arguments: -/// '(' 'of' ':' decl-name (',' differentiation-params-clause)? ')' +/// '(' 'of' ':' qualified-decl-name (',' differentiability-params-clause)? +/// ')' /// \endverbatim ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "derivative"; SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; DeclNameRefWithLoc original; - SmallVector params; + SmallVector parameters; // Parse trailing comma, if it exists, and check for errors. auto consumeIfTrailingComma = [&]() -> bool { @@ -1124,21 +1179,17 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, return makeParserError(); } { - // Parse the name of the function. - SyntaxParsingContext FuncDeclNameContext(SyntaxContext, - SyntaxKind::FunctionDeclName); - // NOTE: Use `afterDot = true` and `allowDeinitAndSubscript = true` to - // enable, e.g. `@derivative(of: init)` and `@derivative(of: subscript)`. - original.Name = parseUnqualifiedDeclName( - /*afterDot*/ true, original.Loc, - diag::attr_derivative_expected_original_name, /*allowOperators*/ true, - /*allowZeroArgCompoundNames*/ true, /*allowDeinitAndSubscript*/ true); + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); } if (consumeIfTrailingComma()) return makeParserError(); - // Parse the optional 'wrt' differentiation parameters clause. + // Parse the optional 'wrt' differentiability parameters clause. if (isIdentifier(Tok, "wrt") && - parseDifferentiationParametersClause(params, AttrName)) + parseDifferentiabilityParametersClause(parameters, AttrName)) return makeParserError(); } // Parse ')'. @@ -1147,9 +1198,85 @@ ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, /*DeclModifier*/ false); return makeParserError(); } - return ParserResult( - DerivativeAttr::create(Context, /*implicit*/ false, atLoc, - SourceRange(loc, rParenLoc), original, params)); + return ParserResult(DerivativeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, parameters)); +} + +/// Parse a `@transpose(of:)` attribute, returning true on error. +/// +/// \verbatim +/// transpose-attribute-arguments: +/// '(' 'of' ':' qualified-decl-name (',' linearity-params-clause)? ')' +/// linearity-params-clause: +/// 'wrt' ':' (linearity-param | linearity-params) +/// linearity-params: +/// '(' linearity-param (',' linearity-param)* ')' +/// linearity-param: +/// 'self' | [0-9]+ +/// \endverbatim +ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, + SourceLoc loc) { + StringRef AttrName = "transpose"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; + DeclNameRefWithLoc original; + SmallVector parameters; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before ')'. + if (Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + // Check that token after comma is 'wrt:'. + if (isIdentifier(Tok, "wrt")) + return false; + diagnose(Tok, diag::attr_expected_label, "wrt", AttrName); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + }; + + // Parse '('. + if (!consumeIf(tok::l_paren, lParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DerivativeRegistrationAttributeArguments); + // Parse the 'of:' label and colon. + if (parseSpecificIdentifier("of", diag::attr_missing_label, "of", + AttrName) || + parseToken(tok::colon, diag::expected_colon_after_label, "of")) { + return makeParserError(); + } + { + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); + } + if (consumeIfTrailingComma()) + return makeParserError(); + // Parse the optional 'wrt' linearity parameters clause. + if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && + parseDifferentiabilityParametersClause(parameters, AttrName, + /*allowNamedParameters*/ false)) + return makeParserError(); + } + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + return ParserResult(TransposeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, parameters)); } void Parser::parseObjCSelector(SmallVector &Names, @@ -2060,10 +2187,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, SyntaxKind::DeclName); DeclNameLoc loc; - replacedFunction = parseUnqualifiedDeclName( - true, loc, diag::attr_dynamic_replacement_expected_function, - /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true, - /*allowDeinitAndSubscript*/ true); + replacedFunction = parseDeclNameRef(loc, + diag::attr_dynamic_replacement_expected_function, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); } } @@ -2123,6 +2251,17 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_Transpose: { + // `@transpose` in a local scope is not allowed. + if (CurDeclContext->isLocalContext()) + diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str()); + + auto Attr = parseTransposeAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + case DAK_ProjectedValueProperty: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -2484,7 +2623,8 @@ static bool parseDifferentiableAttributeArgument(Parser &P, if (P.Tok.is(tok::l_paren)) { backtrack.cancelBacktrack(); if (emitDiagnostics) - P.diagnose(P.Tok, diag::differentiable_attribute_expected_rparen); + P.diagnose(P.Tok, diag::attr_expected_rparen, "@differentiable", + /*DeclModifier*/ false); return true; } return false; @@ -2500,7 +2640,7 @@ static bool parseDifferentiableAttributeArgument(Parser &P, if (argument.getText() != "linear") { if (emitDiagnostics) - P.diagnose(argument, diag::unexpected_argument_differentiable, + P.diagnose(argument, diag::attr_differentiable_unexpected_argument, argument.getText()); return true; } @@ -2558,8 +2698,7 @@ bool Parser::parseConventionAttributeInternal( return true; } if (auto ty = getStringLiteralIfNotInterpolated(Tok.getLoc(), "(C type)")) { - convention.ClangType = ty.getValue(); - convention.ClangTypeLoc = Tok.getLoc(); + convention.ClangType = { ty.getValue(), Tok.getLoc() }; } consumeToken(tok::string_literal); } @@ -2572,11 +2711,9 @@ bool Parser::parseConventionAttributeInternal( return true; } - DeclNameLoc unusedWitnessMethodProtocolLoc; - convention.WitnessMethodProtocol = parseUnqualifiedDeclBaseName( - /*afterDot=*/false, unusedWitnessMethodProtocolLoc, - diag::convention_attribute_witness_method_expected_protocol - ); + DeclNameLoc unusedLoc; + convention.WitnessMethodProtocol = parseDeclNameRef(unusedLoc, + diag::convention_attribute_witness_method_expected_protocol, {}); } // Parse the ')'. We can't use parseMatchingToken if we're in @@ -3298,7 +3435,8 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, SourceLoc BeginLoc = Tok.getLoc(); State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::Decl, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::Decl, Flags.toRaw(), CurDeclContext, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); @@ -3335,6 +3473,18 @@ void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { } } +/// Set the original declaration in `@differentiable` attributes. +/// +/// Necessary because `Parser::parseNewDeclAttribute` (which calls +/// `Parser::parseDifferentiableAttribute`) does not have access to the +/// parent declaration of parsed attributes. +static void +setOriginalDeclarationForDifferentiableAttributes(DeclAttributes attrs, + Decl *D) { + for (auto *attr : attrs.getAttributes()) + const_cast(attr)->setOriginalDeclaration(D); +} + /// Parse a single syntactic declaration and return a list of decl /// ASTs. This can return multiple results for var decls that bind to multiple /// values, structs that define a struct decl and a constructor, etc. @@ -3735,7 +3885,8 @@ Parser::parseDecl(ParseDeclOptions Flags, if (DeclResult.isNonNull()) { Decl *D = DeclResult.get(); if (!declWasHandledAlready(D)) - Handler(DeclResult.get()); + Handler(D); + setOriginalDeclarationForDifferentiableAttributes(D->getAttrs(), D); } if (!DeclResult.isParseError()) { @@ -3814,7 +3965,7 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { return { }; } - auto BeginParserPosition = getParserPosition({BodyRange.Start,BodyRange.End}); + auto BeginParserPosition = getParserPosition({BodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(BodyRange.End); // ParserPositionRAII needs a primed parser to restore to. @@ -3926,7 +4077,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, KindLoc = consumeToken(); } - std::vector> ImportPath; + std::vector> ImportPath; bool HasNext; do { SyntaxParsingContext AccessCompCtx(SyntaxContext, @@ -3938,8 +4089,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } return makeParserCodeCompletionStatus(); } - ImportPath.push_back(std::make_pair(Identifier(), Tok.getLoc())); - if (parseAnyIdentifier(ImportPath.back().first, + ImportPath.push_back({Identifier(), Tok.getLoc()}); + if (parseAnyIdentifier(ImportPath.back().Item, diag::expected_identifier_in_decl, "import")) return nullptr; HasNext = consumeIf(tok::period); @@ -3952,8 +4103,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, // We omit the code completion token if it immediately follows the module // identifiers. auto BufferId = SourceMgr.getCodeCompletionBufferID(); - auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().second, - BufferId) + ImportPath.back().first.str().size(); + auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().Loc, + BufferId) + ImportPath.back().Item.str().size(); auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr. getCodeCompletionLoc(), BufferId); if (IdEndOffset == CCTokenOffset) { @@ -3962,7 +4113,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } if (Kind != ImportKind::Module && ImportPath.size() == 1) { - diagnose(ImportPath.front().second, diag::decl_expected_module_name); + diagnose(ImportPath.front().Loc, diag::decl_expected_module_name); return nullptr; } @@ -4303,8 +4454,6 @@ bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, RBLoc = Tok.getLoc(); error = true; } - - State->delayDeclList(IDC); return error; } @@ -5445,6 +5594,11 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, accessors.record(*this, PrimaryVar, Invalid); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + return makeParserResult(PrimaryVar); } @@ -5700,6 +5854,10 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + + // Set original declaration in `@differentiable` attributes. + setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); + Decls.push_back(VD); if (hasOpaqueReturnTy && sf && !InInactiveClauseEnvironment) { sf->addUnvalidatedDeclWithOpaqueResultType(VD); @@ -5875,7 +6033,8 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, if (isCodeCompletionFirstPass()) { if (SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::FunctionBody, PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc); } else { AFD->setBodySkipped(BodyRange); @@ -6070,19 +6229,6 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, /// Parse function body into \p AFD. void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { - Scope S(this, ScopeKind::FunctionBody); - - // Enter the arguments for the function into a new function-body scope. We - // need this even if there is no function body to detect argument name - // duplication. - if (auto *P = AFD->getImplicitSelfDecl()) - addToScope(P); - addParametersToScope(AFD->getParameters()); - - // Establish the new context. - ParseFunctionBody CC(*this, AFD); - setLocalDiscriminatorToParamList(AFD->getParameters()); - if (!Tok.is(tok::l_brace)) { checkForInputIncomplete(); return; @@ -6100,6 +6246,19 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { return; } + Scope S(this, ScopeKind::FunctionBody); + + // Enter the arguments for the function into a new function-body scope. We + // need this even if there is no function body to detect argument name + // duplication. + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); + + // Establish the new context. + ParseFunctionBody CC(*this, AFD); + setLocalDiscriminatorToParamList(AFD->getParameters()); + if (Context.Stats) Context.Stats->getFrontendCounters().NumFunctionsParsed++; @@ -6163,7 +6322,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { "function body should be delayed"); auto bodyRange = AFD->getBodySourceRange(); - auto BeginParserPosition = getParserPosition({bodyRange.Start,bodyRange.End}); + auto BeginParserPosition = getParserPosition({bodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc()); // ParserPositionRAII needs a primed parser to restore to. @@ -6185,6 +6344,9 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { // Re-enter the lexical scope. Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); @@ -6943,6 +7105,11 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, accessors.record(*this, Subscript, (Invalid || !Status.isSuccess())); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + // No need to setLocalDiscriminator because subscripts cannot // validly appear outside of type decls. return makeParserResult(Status, Subscript); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b65596824c059..6362bc2454b02 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -644,6 +644,7 @@ ParserResult Parser::parseExprKeyPathObjC() { // Parse the sequence of unqualified-names. ParserStatus status; SourceLoc LastDotLoc; + DeclNameOptions flags = DeclNameFlag::AllowCompoundNames; while (true) { SyntaxParsingContext NamePieceCtx(SyntaxContext, SyntaxKind::ObjcNamePiece); // Handle code completion. @@ -652,10 +653,8 @@ ParserResult Parser::parseExprKeyPathObjC() { // Parse the next name. DeclNameLoc nameLoc; - bool afterDot = !components.empty(); - auto name = parseUnqualifiedDeclName( - afterDot, nameLoc, - diag::expr_keypath_expected_property_or_type); + DeclNameRef name = parseDeclNameRef(nameLoc, + diag::expr_keypath_expected_property_or_type, flags); if (!name) { status.setIsParseError(); break; @@ -666,6 +665,9 @@ ParserResult Parser::parseExprKeyPathObjC() { nameLoc.getBaseNameLoc()); components.push_back(component); + // After the first component, we can start parsing keywords. + flags |= DeclNameFlag::AllowKeywords; + // Handle code completion. if (Tok.is(tok::code_complete)) return handleCodeCompletion(SourceLoc()); @@ -1130,7 +1132,8 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Diag<> D = isa(Result.get()) ? diag::expected_identifier_after_super_dot_expr : diag::expected_member_name; - DeclNameRef Name = parseUnqualifiedDeclName(/*afterDot=*/true, NameLoc, D); + auto Name = parseDeclNameRef(NameLoc, D, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); if (!Name) return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -1580,8 +1583,8 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { return Result; } - Name = parseUnqualifiedDeclName(/*afterDot=*/true, NameLoc, - diag::expected_identifier_after_dot_expr); + Name = parseDeclNameRef(NameLoc, diag::expected_identifier_after_dot_expr, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); if (!Name) return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -2078,12 +2081,87 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { } } -DeclNameRef Parser::parseUnqualifiedDeclBaseName( - bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowDeinitAndSubscript) { +static bool tryParseArgLabelList(Parser &P, Parser::DeclNameOptions flags, + SourceLoc &lparenLoc, + SmallVectorImpl &argumentLabels, + SmallVectorImpl &argumentLabelLocs, + SourceLoc &rparenLoc) { + if (!flags.contains(Parser::DeclNameFlag::AllowCompoundNames)) + return false; + + // Is the current token a left paren? + if (!P.Tok.isFollowingLParen()) + return false; + + // Okay, let's look ahead and see if the next token is something that could + // be in an arg label list... + const Token &next = P.peekToken(); + + // A close parenthesis, if empty lists are allowed. + bool nextIsRParen = + flags.contains(Parser::DeclNameFlag::AllowZeroArgCompoundNames) && + next.is(tok::r_paren); + // An argument label. + bool nextIsArgLabel = next.canBeArgumentLabel() || next.is(tok::colon); + // An editor placeholder. + bool nextIsPlaceholder = Identifier::isEditorPlaceholder(next.getText()); + + if (!(nextIsRParen || nextIsArgLabel || nextIsPlaceholder)) + return false; + + // Try to parse a compound name. + SyntaxParsingContext ArgsCtxt(P.SyntaxContext, SyntaxKind::DeclNameArguments); + Parser::BacktrackingScope backtrack(P); + + lparenLoc = P.consumeToken(tok::l_paren); + while (P.Tok.isNot(tok::r_paren)) { + SyntaxParsingContext ArgCtxt(P.SyntaxContext, SyntaxKind::DeclNameArgument); + + // If we see a ':', the user forgot the '_'; + if (P.Tok.is(tok::colon)) { + P.diagnose(P.Tok, diag::empty_arg_label_underscore) + .fixItInsert(P.Tok.getLoc(), "_"); + argumentLabels.push_back(Identifier()); + argumentLabelLocs.push_back(P.consumeToken(tok::colon)); + } + + Identifier argName; + SourceLoc argLoc; + P.parseOptionalArgumentLabel(argName, argLoc); + if (argLoc.isValid()) { + argumentLabels.push_back(argName); + argumentLabelLocs.push_back(argLoc); + continue; + } + + // This is not a compound name. + // FIXME: Could recover better if we "know" it's a compound name. + ArgCtxt.setBackTracking(); + ArgsCtxt.setBackTracking(); + + return false; + } + + // We have a compound name. Cancel backtracking and build that name. + backtrack.cancelBacktrack(); + + if (argumentLabels.empty() && P.SyntaxContext->isEnabled()) + P.SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankDeclNameArgumentList( + P.leadingTriviaLoc(), *P.SyntaxContext)); + else + ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList); + + rparenLoc = P.consumeToken(tok::r_paren); + + assert(argumentLabels.size() == argumentLabelLocs.size()); + + return true; +} + +DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, + const Diagnostic &diag, + DeclNameOptions flags) { // Consume the base name. DeclBaseName baseName; SourceLoc baseNameLoc; @@ -2092,17 +2170,21 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName( baseNameLoc = consumeIdentifier( &baseNameId, /*allowDollarIdentifier=*/true); baseName = baseNameId; - } else if (allowOperators && Tok.isAnyOperator()) { + } else if (flags.contains(DeclNameFlag::AllowOperators) && + Tok.isAnyOperator()) { baseName = Context.getIdentifier(Tok.getText()); baseNameLoc = consumeToken(); - } else if (afterDot && Tok.isKeyword()) { + } else if (flags.contains(DeclNameFlag::AllowKeywords) && Tok.isKeyword()) { + bool specialDeinitAndSubscript = + flags.contains(DeclNameFlag::AllowKeywordsUsingSpecialNames); + // Syntax highlighting should treat this token as an identifier and // not as a keyword. if (Tok.is(tok::kw_init)) baseName = DeclBaseName::createConstructor(); - else if (allowDeinitAndSubscript &&Tok.is(tok::kw_deinit)) + else if (specialDeinitAndSubscript && Tok.is(tok::kw_deinit)) baseName = DeclBaseName::createDestructor(); - else if (allowDeinitAndSubscript &&Tok.is(tok::kw_subscript)) + else if (specialDeinitAndSubscript && Tok.is(tok::kw_subscript)) baseName = DeclBaseName::createSubscript(); else baseName = Context.getIdentifier(Tok.getText()); @@ -2115,95 +2197,26 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName( return DeclNameRef(); } - loc = DeclNameLoc(baseNameLoc); - return DeclNameRef(baseName); -} - - -DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot, - DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowZeroArgCompoundNames, - bool allowDeinitAndSubscript) { - // Consume the base name. - auto baseName = parseUnqualifiedDeclBaseName(afterDot, loc, diag, - allowOperators, - allowDeinitAndSubscript); - - // If the next token isn't a following '(', we don't have a compound name. - if (!baseName || !Tok.isFollowingLParen()) - return baseName; - - // If the next token is a ')' then we have a 0-arg compound name. This is - // explicitly differentiated from "simple" (non-compound) name in DeclName. - // Unfortunately only some places in the grammar are ok with accepting this - // kind of name; in other places it's ambiguous with trailing calls. - if (allowZeroArgCompoundNames && peekToken().is(tok::r_paren)) { - SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments); - consumeToken(tok::l_paren); - if (SyntaxContext->isEnabled()) - SyntaxContext->addSyntax( - ParsedSyntaxRecorder::makeBlankDeclNameArgumentList( - leadingTriviaLoc(), *SyntaxContext)); - consumeToken(tok::r_paren); - SmallVector argumentLabels; - return baseName.withArgumentLabels(Context, argumentLabels); - } - - // If the token after that isn't an argument label or ':', we don't have a - // compound name. - if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) || - Identifier::isEditorPlaceholder(peekToken().getText())) { - return baseName; - } - - // Try to parse a compound name. - SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments); - BacktrackingScope backtrack(*this); - + // Parse an argument list, if the flags allow it and it's present. SmallVector argumentLabels; SmallVector argumentLabelLocs; - SourceLoc lparenLoc = consumeToken(tok::l_paren); + SourceLoc lparenLoc; SourceLoc rparenLoc; - while (Tok.isNot(tok::r_paren)) { - SyntaxParsingContext ArgCtxt(SyntaxContext, SyntaxKind::DeclNameArgument); - - // If we see a ':', the user forgot the '_'; - if (Tok.is(tok::colon)) { - diagnose(Tok, diag::empty_arg_label_underscore) - .fixItInsert(Tok.getLoc(), "_"); - argumentLabels.push_back(Identifier()); - argumentLabelLocs.push_back(consumeToken(tok::colon)); - } - - Identifier argName; - SourceLoc argLoc; - parseOptionalArgumentLabel(argName, argLoc); - if (argLoc.isValid()) { - argumentLabels.push_back(argName); - argumentLabelLocs.push_back(argLoc); - continue; - } - // This is not a compound name. - // FIXME: Could recover better if we "know" it's a compound name. - ArgCtxt.setBackTracking(); - ArgsCtxt.setBackTracking(); - return baseName; - } - // We have a compound name. Cancel backtracking and build that name. - backtrack.cancelBacktrack(); + bool hadArgList = tryParseArgLabelList(*this, flags, lparenLoc, + argumentLabels, argumentLabelLocs, + rparenLoc); - ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList); - rparenLoc = consumeToken(tok::r_paren); + if (argumentLabelLocs.empty() || !hadArgList) + loc = DeclNameLoc(baseNameLoc); + else + loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs, + rparenLoc); - assert(!argumentLabels.empty() && "Logic above should prevent this"); - assert(argumentLabels.size() == argumentLabelLocs.size()); + if (!hadArgList) + return DeclNameRef(baseName); - loc = DeclNameLoc(Context, loc.getBaseNameLoc(), lparenLoc, argumentLabelLocs, - rparenLoc); - return baseName.withArgumentLabels(Context, argumentLabels); + return DeclNameRef({ Context, baseName, argumentLabels }); } /// expr-identifier: @@ -2216,8 +2229,8 @@ Expr *Parser::parseExprIdentifier() { // Parse the unqualified-decl-name. DeclNameLoc loc; - DeclNameRef name = parseUnqualifiedDeclName(/*afterDot=*/false, loc, - diag::expected_expr); + DeclNameRef name = parseDeclNameRef(loc, diag::expected_expr, + DeclNameFlag::AllowCompoundNames); SmallVector args; SourceLoc LAngleLoc, RAngleLoc; @@ -2390,11 +2403,15 @@ static void printTupleNames(const TypeRepr *typeRepr, llvm::raw_ostream &OS) { } bool Parser:: -parseClosureSignatureIfPresent(SmallVectorImpl &captureList, +parseClosureSignatureIfPresent(SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfDecl, ParameterList *¶ms, SourceLoc &throwsLoc, SourceLoc &arrowLoc, TypeRepr *&explicitResultType, SourceLoc &inLoc){ // Clear out result parameters. + bracketRange = SourceRange(); + capturedSelfDecl = nullptr; params = nullptr; throwsLoc = SourceLoc(); arrowLoc = SourceLoc(); @@ -2463,14 +2480,16 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, } SyntaxParsingContext ClosureSigCtx(SyntaxContext, SyntaxKind::ClosureSignature); if (Tok.is(tok::l_square) && peekToken().is(tok::r_square)) { + SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); - consumeToken(tok::r_square); + SourceLoc lBracketLoc = consumeToken(tok::l_square); + SourceLoc rBracketLoc = consumeToken(tok::r_square); + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } else if (Tok.is(tok::l_square) && !peekToken().is(tok::r_square)) { SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); + SourceLoc lBracketLoc = consumeToken(tok::l_square); // At this point, we know we have a closure signature. Parse the capture list // and parameters. bool HasNext; @@ -2560,6 +2579,10 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, auto *VD = new (Context) VarDecl(/*isStatic*/false, introducer, /*isCaptureList*/true, nameLoc, name, CurDeclContext); + + // If we captured something under the name "self", remember that. + if (name == Context.Id_self) + capturedSelfDecl = VD; // Attributes. if (ownershipKind != ReferenceOwnership::Strong) @@ -2573,17 +2596,22 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, /*VarLoc*/ nameLoc, pattern, /*EqualLoc*/ equalLoc, initializer, CurDeclContext); - captureList.push_back(CaptureListEntry(VD, PBD)); + auto CLE = CaptureListEntry(VD, PBD); + if (CLE.isSimpleSelfCapture()) + VD->setIsSelfParamCapture(); + captureList.push_back(CLE); } while (HasNext); SyntaxContext->collectNodesInPlace(SyntaxKind::ClosureCaptureItemList); // The capture list needs to be closed off with a ']'. + SourceLoc rBracketLoc = Tok.getLoc(); if (!consumeIf(tok::r_square)) { diagnose(Tok, diag::expected_capture_list_end_rsquare); skipUntil(tok::r_square); if (Tok.is(tok::r_square)) - consumeToken(tok::r_square); + rBracketLoc = consumeToken(tok::r_square); } + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } bool invalid = false; @@ -2741,14 +2769,17 @@ ParserResult Parser::parseExprClosure() { SourceLoc leftBrace = consumeToken(); // Parse the closure-signature, if present. + SourceRange bracketRange; + SmallVector captureList; + VarDecl *capturedSelfDecl; ParameterList *params = nullptr; SourceLoc throwsLoc; SourceLoc arrowLoc; TypeRepr *explicitResultType; SourceLoc inLoc; - SmallVector captureList; - parseClosureSignatureIfPresent(captureList, params, throwsLoc, arrowLoc, - explicitResultType, inLoc); + parseClosureSignatureIfPresent(bracketRange, captureList, + capturedSelfDecl, params, throwsLoc, + arrowLoc, explicitResultType, inLoc); // If the closure was created in the context of an array type signature's // size expression, there will not be a local context. A parse error will @@ -2763,9 +2794,10 @@ ParserResult Parser::parseExprClosure() { unsigned discriminator = CurLocalContext->claimNextClosureDiscriminator(); // Create the closure expression and enter its context. - auto *closure = new (Context) ClosureExpr(params, throwsLoc, arrowLoc, inLoc, - explicitResultType, - discriminator, CurDeclContext); + auto *closure = new (Context) ClosureExpr(bracketRange, capturedSelfDecl, + params, throwsLoc, arrowLoc, inLoc, + explicitResultType, discriminator, + CurDeclContext); // The arguments to the func are defined in their own scope. Scope S(this, ScopeKind::ClosureParams); ParseFunctionBody cc(*this, closure); @@ -2780,7 +2812,7 @@ ParserResult Parser::parseExprClosure() { // FIXME: We could do this all the time, and then provide Fix-Its // to map $i -> the appropriately-named argument. This might help // users who are refactoring code by adding names. - AnonClosureVars.push_back({ leftBrace, {}}); + AnonClosureVars.push_back({{}, leftBrace}); } // Add capture list variables to scope. @@ -2804,7 +2836,7 @@ ParserResult Parser::parseExprClosure() { // anonymous closure arguments. if (!params) { // Create a parameter pattern containing the anonymous variables. - auto &anonVars = AnonClosureVars.back().second; + auto &anonVars = AnonClosureVars.back().Item; SmallVector elements; for (auto anonVar : anonVars) elements.push_back(anonVar); @@ -2917,8 +2949,8 @@ Expr *Parser::parseExprAnonClosureArg() { } } - auto leftBraceLoc = AnonClosureVars.back().first; - auto &decls = AnonClosureVars.back().second; + auto leftBraceLoc = AnonClosureVars.back().Loc; + auto &decls = AnonClosureVars.back().Item; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); SmallVector StrBuf; @@ -3031,9 +3063,8 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SyntaxParsingContext operatorContext(SyntaxContext, SyntaxKind::IdentifierExpr); DeclNameLoc Loc; - auto OperName = parseUnqualifiedDeclBaseName(/*afterDot=*/false, Loc, - diag::expected_operator_ref, - /*allowOperators=*/true); + auto OperName = parseDeclNameRef(Loc, diag::expected_operator_ref, + DeclNameFlag::AllowOperators); if (!OperName) { return makeParserError(); } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index ff625a4326f79..d6ed9a2d6ea83 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -167,10 +167,6 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) { } } - if (ResultExpr.hasCodeCompletion() && CodeCompletion) { - CodeCompletion->completeExpr(); - } - return ResultExpr; } @@ -202,6 +198,8 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, ArrayRef ParsedDecls) { switch (Kind) { case BraceItemListKind::Brace: + case BraceItemListKind::TopLevelCode: + case BraceItemListKind::TopLevelLibrary: return false; case BraceItemListKind::Case: { if (Tok.is(tok::pound_if)) { @@ -220,26 +218,6 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, } return isAtStartOfSwitchCase(*this); } - case BraceItemListKind::TopLevelCode: - // When parsing the top level executable code for a module, if we parsed - // some executable code, then we're done. We want to process (name bind, - // type check, etc) decls one at a time to make sure that there are not - // forward type references, etc. There is an outer loop around the parser - // that will reinvoke the parser at the top level on each statement until - // EOF. In contrast, it is ok to have forward references between classes, - // functions, etc. - for (auto I : ParsedDecls) { - if (isa(I.get())) - // Only bail out if the next token is at the start of a line. If we - // don't, then we may accidentally allow things like "a = 1 b = 4". - // FIXME: This is really dubious. This will reject some things, but - // allow other things we don't want. - if (Tok.isAtStartOfLine()) - return true; - } - return false; - case BraceItemListKind::TopLevelLibrary: - return false; case BraceItemListKind::ActiveConditionalBlock: case BraceItemListKind::InactiveConditionalBlock: return Tok.isNot(tok::pound_else) && Tok.isNot(tok::pound_endif) && @@ -257,7 +235,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); State->setCodeCompletionDelayedDeclState( - PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl, + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::TopLevelCodeDecl, PD_Default, TLCD, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); // Skip the rest of the file to prevent the parser from constructing the AST @@ -288,7 +267,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, /// expr '=' expr ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, BraceItemListKind Kind, - BraceItemListKind ConditionalBlockKind) { + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard) { bool isRootCtx = SyntaxContext->isRoot(); SyntaxParsingContext ItemListContext(SyntaxContext, SyntaxKind::CodeBlockItemList); @@ -381,7 +361,8 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, [&](SmallVectorImpl &Elements, bool IsActive) { parseBraceItems(Elements, Kind, IsActive ? BraceItemListKind::ActiveConditionalBlock - : BraceItemListKind::InactiveConditionalBlock); + : BraceItemListKind::InactiveConditionalBlock, + IsFollowingGuard); }); if (IfConfigResult.hasCodeCompletion() && isCodeCompletionFirstPass()) { consumeDecl(BeginParserPosition, None, IsTopLevel); @@ -418,7 +399,18 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, ParserResult DeclResult = parseDecl(IsTopLevel ? PD_AllowTopLevel : PD_Default, IsAtStartOfLineOrPreviousHadSemi, - [&](Decl *D) {TmpDecls.push_back(D);}); + [&](Decl *D) { + TmpDecls.push_back(D); + + // Any function after a 'guard' statement is marked as + // possibly having local captures. This allows SILGen + // to correctly determine its capture list, since + // otherwise it would be skipped because it is not + // defined inside a local context. + if (IsFollowingGuard) + if (auto *FD = dyn_cast(D)) + FD->setHasTopLevelLocalContextCaptures(); + }); BraceItemsStatus |= DeclResult; if (DeclResult.isParseError()) { NeedParseErrorRecovery = true; @@ -468,6 +460,14 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, Result, Result.getEndLoc()); TLCD->setBody(Brace); Entries.push_back(TLCD); + + // A top-level 'guard' statement can introduce local bindings, so we + // must mark all functions following one. This makes them behave + // as if they were in local context for the purposes of capture + // emission in SILGen. + if (auto *stmt = Result.dyn_cast()) + if (isa(stmt)) + IsFollowingGuard = true; } } else if (Tok.is(tok::kw_init) && isa(CurDeclContext)) { SourceLoc StartLoc = Tok.getLoc(); @@ -1917,18 +1917,21 @@ ParserResult Parser::parseStmtDo(LabeledStmtInfo labelInfo) { DoCatchStmt::create(Context, labelInfo, doLoc, body.get(), allClauses)); } - SourceLoc whileLoc; - - // If we don't see a 'while', this is just the bare 'do' scoping - // statement. - if (!consumeIf(tok::kw_while, whileLoc)) { + // If we dont see a 'while' or see a 'while' that starts + // from new line. This is just the bare `do` scoping statement. + if (Tok.getKind() != tok::kw_while || Tok.isAtStartOfLine()) { return makeParserResult(status, - new (Context) DoStmt(labelInfo, doLoc, body.get())); + new (Context) DoStmt(labelInfo, doLoc, body.get())); } - + SourceLoc whileLoc = Tok.getLoc(); // But if we do, advise the programmer that it's 'repeat' now. - diagnose(doLoc, diag::do_while_now_repeat_while) + diagnose(doLoc, diag::do_while_now_repeat_while); + diagnose(doLoc, diag::do_while_expected_repeat_while) .fixItReplace(doLoc, "repeat"); + diagnose(doLoc, diag::do_while_expected_separate_stmt) + .fixItInsert(whileLoc, "\n"); + + consumeToken(tok::kw_while); status.setIsParseError(); ParserResult condition; if (Tok.is(tok::l_brace)) { diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 6d7e84cd2c61f..58149f6e8b014 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -638,7 +638,13 @@ ParserStatus Parser::parseGenericArguments(SmallVectorImpl &Args, /// type-identifier: /// identifier generic-args? ('.' identifier generic-args?)* /// -ParserResult Parser::parseTypeIdentifier() { +ParserResult +Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { + // If parsing a qualified declaration name, return error if base type cannot + // be parsed. + if (isParsingQualifiedDeclBaseType && !canParseBaseTypeForQualifiedDeclName()) + return makeParserError(); + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { // is this the 'Any' type if (Tok.is(tok::kw_Any)) { @@ -667,9 +673,8 @@ ParserResult Parser::parseTypeIdentifier() { SourceLoc EndLoc; while (true) { DeclNameLoc Loc; - DeclNameRef Name = parseUnqualifiedDeclBaseName( - /*afterDot=*/false, Loc, - diag::expected_identifier_in_dotted_type); + DeclNameRef Name = + parseDeclNameRef(Loc, diag::expected_identifier_in_dotted_type, {}); if (!Name) Status.setIsParseError(); @@ -704,6 +709,18 @@ ParserResult Parser::parseTypeIdentifier() { } if (!peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { + // If parsing a qualified declaration name, break before parsing the + // period before the final declaration name component. + if (isParsingQualifiedDeclBaseType) { + // If qualified name base type cannot be parsed from the current + // point (i.e. the next type identifier is not followed by a '.'), + // then the next identifier is the final declaration name component. + BacktrackingScope backtrack(*this); + consumeStartingCharacterOfCurrentToken(tok::period); + if (!canParseBaseTypeForQualifiedDeclName()) + break; + } + // Consume the period. consumeToken(); continue; } @@ -1542,19 +1559,27 @@ bool Parser::canParseTypeIdentifierOrTypeComposition() { } } +bool Parser::canParseSimpleTypeIdentifier() { + // Parse an identifier. + if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + return false; + consumeToken(); + + // Parse an optional generic argument list. + if (startsWithLess(Tok)) + if (!canParseGenericArguments()) + return false; + + return true; +} + bool Parser::canParseTypeIdentifier() { while (true) { - if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + if (!canParseSimpleTypeIdentifier()) return false; - consumeToken(); - - if (startsWithLess(Tok)) { - if (!canParseGenericArguments()) - return false; - } // Treat 'Foo.' as an attempt to write a dotted type - // unless is 'Type'. + // unless is 'Type' or 'Protocol'. if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && !peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { @@ -1565,6 +1590,17 @@ bool Parser::canParseTypeIdentifier() { } } +bool Parser::canParseBaseTypeForQualifiedDeclName() { + BacktrackingScope backtrack(*this); + + // Parse a simple type identifier. + if (!canParseSimpleTypeIdentifier()) + return false; + + // Qualified name base types must be followed by a period. + // If the next token starts with a period, return true. + return startsWithSymbol(Tok, '.'); +} bool Parser::canParseOldStyleProtocolComposition() { consumeToken(tok::kw_protocol); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 083ba4fa9f3d1..1a76ec7359319 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -113,12 +113,6 @@ void SILParserTUStateBase::anchor() { } void swift::performCodeCompletionSecondPass( PersistentParserState &ParserState, CodeCompletionCallbacksFactory &Factory) { - Parser::performCodeCompletionSecondPass(ParserState, Factory); -} - -void Parser::performCodeCompletionSecondPass( - PersistentParserState &ParserState, - CodeCompletionCallbacksFactory &Factory) { if (!ParserState.hasCodeCompletionDelayedDeclState()) return; @@ -128,7 +122,7 @@ void Parser::performCodeCompletionSecondPass( FrontendStatsTracer tracer(Ctx.Stats, "CodeCompletionSecondPass"); - auto BufferID = Ctx.SourceMgr.findBufferContainingLoc(state->BodyPos.Loc); + auto BufferID = Ctx.SourceMgr.getCodeCompletionBufferID(); Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); std::unique_ptr CodeCompletion( @@ -139,12 +133,17 @@ void Parser::performCodeCompletionSecondPass( } void Parser::performCodeCompletionSecondPassImpl( - PersistentParserState::CodeCompletionDelayedDeclState &info) { + CodeCompletionDelayedDeclState &info) { // Disable libSyntax creation in the delayed parsing. SyntaxContext->disable(); + auto BufferID = L->getBufferID(); + auto startLoc = SourceMgr.getLocForOffset(BufferID, info.StartOffset); + SourceLoc prevLoc; + if (info.PrevOffset != ~0U) + prevLoc = SourceMgr.getLocForOffset(BufferID, info.PrevOffset); // Set the parser position to the start of the delayed decl or the body. - restoreParserPosition(getParserPosition(info.BodyPos)); + restoreParserPosition(getParserPosition({startLoc, prevLoc})); // Do not delay parsing in the second pass. llvm::SaveAndRestore DisableDelayedBody(DelayBodyParsing, false); @@ -155,7 +154,7 @@ void Parser::performCodeCompletionSecondPassImpl( DeclContext *DC = info.ParentContext; switch (info.Kind) { - case PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { + case CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { // Re-enter the top-level code decl context. // FIXME: this can issue discriminators out-of-order? auto *TLCD = cast(DC); @@ -171,7 +170,7 @@ void Parser::performCodeCompletionSecondPassImpl( break; } - case PersistentParserState::CodeCompletionDelayedDeclKind::Decl: { + case CodeCompletionDelayedDeclKind::Decl: { assert((DC->isTypeContext() || DC->isModuleScopeContext()) && "Delayed decl must be a type member or a top-level decl"); ContextChange CC(*this, DC); @@ -183,7 +182,7 @@ void Parser::performCodeCompletionSecondPassImpl( } else if (auto *ED = dyn_cast(DC)) { ED->addMember(D); } else if (auto *SF = dyn_cast(DC)) { - SF->Decls.push_back(D); + SF->addTopLevelDecl(D); } else { llvm_unreachable("invalid decl context kind"); } @@ -191,8 +190,13 @@ void Parser::performCodeCompletionSecondPassImpl( break; } - case PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody: { + case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); + + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); + ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); @@ -206,6 +210,8 @@ void Parser::performCodeCompletionSecondPassImpl( "Second pass should not set any code completion info"); CodeCompletion->doneParsing(); + + State->restoreCodeCompletionDelayedDeclState(info); } swift::Parser::BacktrackingScope::~BacktrackingScope() { @@ -431,11 +437,11 @@ class TokenRecorder: public ConsumeTokenReceiver { } public: - TokenRecorder(SourceFile &SF): + TokenRecorder(SourceFile &SF, unsigned BufferID): Ctx(SF.getASTContext()), SM(SF.getASTContext().SourceMgr), Bag(SF.getTokenVector()), - BufferID(SF.getBufferID().getValue()) {}; + BufferID(BufferID) {}; void finalize() override { @@ -517,7 +523,7 @@ Parser::Parser(std::unique_ptr Lex, SourceFile &SF, Context(SF.getASTContext()), DelayBodyParsing(DelayBodyParsing), TokReceiver(SF.shouldCollectToken() ? - new TokenRecorder(SF) : + new TokenRecorder(SF, L->getBufferID()) : new ConsumeTokenReceiver()), SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, L->getBufferID(), @@ -1048,7 +1054,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, while (true) { while (Tok.is(tok::comma)) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(Tok.getLoc())); + .fixItRemove(Tok.getLoc()); consumeToken(); } SourceLoc StartLoc = Tok.getLoc(); @@ -1078,7 +1084,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, continue; if (!AllowSepAfterLast) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(PreviousLoc)); + .fixItRemove(PreviousLoc); } break; } diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 5244697ecba2d..4e4d15e4dd072 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/Basic/SourceManager.h" #include "swift/Parse/PersistentParserState.h" using namespace swift; @@ -26,20 +27,26 @@ PersistentParserState::PersistentParserState() { } PersistentParserState::~PersistentParserState() { } void PersistentParserState::setCodeCompletionDelayedDeclState( - CodeCompletionDelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc) { + SourceManager &SM, unsigned BufferID, CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, + SourceLoc PreviousLoc) { assert(!CodeCompletionDelayedDeclStat.get() && "only one decl can be delayed for code completion"); - CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( - Kind, Flags, ParentContext, BodyRange, PreviousLoc, - ScopeInfo.saveCurrentScope())); -} + unsigned startOffset = SM.getLocOffsetInBuffer(BodyRange.Start, BufferID); + unsigned endOffset = SM.getLocOffsetInBuffer(BodyRange.End, BufferID); + unsigned prevOffset = ~0U; + if (PreviousLoc.isValid()) + prevOffset = SM.getLocOffsetInBuffer(PreviousLoc, BufferID); -void PersistentParserState::delayDeclList(IterableDeclContext *D) { - DelayedDeclLists.push_back(D); + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( + Kind, Flags, ParentContext, ScopeInfo.saveCurrentScope(), startOffset, + endOffset, prevOffset)); } -void PersistentParserState::parseAllDelayedDeclLists() { - for (auto IDC : DelayedDeclLists) - IDC->loadAllMembers(); +void PersistentParserState::restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other) { + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( + other.Kind, other.Flags, other.ParentContext, + ScopeInfo.saveCurrentScope(), other.StartOffset, other.EndOffset, + other.PrevOffset)); } diff --git a/lib/Parse/Scope.cpp b/lib/Parse/Scope.cpp index d8ebb202d4b91..0ae1ff976c196 100644 --- a/lib/Parse/Scope.cpp +++ b/lib/Parse/Scope.cpp @@ -50,14 +50,14 @@ static bool isResolvableScope(ScopeKind SK) { } Scope::Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock) - : SI(P->getScopeInfo()), - HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), - PrevScope(SI.CurScope), - PrevResolvableDepth(SI.ResolvableDepth), - Kind(SC), - IsInactiveConfigBlock(isInactiveConfigBlock) { + : Scope(P->getScopeInfo(), SC, isInactiveConfigBlock) {} + +Scope::Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock) + : SI(SI), HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), + PrevScope(SI.CurScope), PrevResolvableDepth(SI.ResolvableDepth), Kind(SC), + IsInactiveConfigBlock(isInactiveConfigBlock) { assert(PrevScope || Kind == ScopeKind::TopLevel); - + if (SI.CurScope) { Depth = SI.CurScope->Depth + 1; IsInactiveConfigBlock |= SI.CurScope->IsInactiveConfigBlock; diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index f20d804df0060..2b71454302577 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -60,7 +60,7 @@ class SILParserTUState : public SILParserTUStateBase { /// This is all of the forward referenced functions with /// the location for where the reference is. llvm::DenseMap> ForwardRefFns; + Located> ForwardRefFns; /// A list of all functions forward-declared by a sil_scope. llvm::DenseSet PotentialZombieFns; @@ -85,8 +85,8 @@ class SILParserTUState : public SILParserTUStateBase { SILParserTUState::~SILParserTUState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { - if (Entry.second.second.isValid()) { - M.getASTContext().Diags.diagnose(Entry.second.second, + if (Entry.second.Loc.isValid()) { + M.getASTContext().Diags.diagnose(Entry.second.Loc, diag::sil_use_of_undefined_value, Entry.first.str()); } @@ -112,13 +112,13 @@ void PrettyStackTraceParser::print(llvm::raw_ostream &out) const { out << '\n'; } -static bool parseIntoSourceFileImpl(SourceFile &SF, - unsigned BufferID, - bool *Done, - SILParserState *SIL, - PersistentParserState *PersistentState, - bool FullParse, - bool DelayBodyParsing) { +static void parseIntoSourceFileImpl(SourceFile &SF, + unsigned BufferID, + bool *Done, + SILParserState *SIL, + PersistentParserState *PersistentState, + bool FullParse, + bool DelayBodyParsing) { assert((!FullParse || (SF.canBeParsedInFull() && !SIL)) && "cannot parse in full with the given parameters!"); @@ -149,10 +149,8 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, llvm::SaveAndRestore S(P.IsParsingInterfaceTokens, SF.hasInterfaceHash()); - bool FoundSideEffects = false; do { - bool hasSideEffects = P.parseTopLevel(); - FoundSideEffects = FoundSideEffects || hasSideEffects; + P.parseTopLevel(); *Done = P.Tok.is(tok::eof); } while (FullParse && !*Done); @@ -160,29 +158,27 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, auto rawNode = P.finalizeSyntaxTree(); STreeCreator->acceptSyntaxRoot(rawNode, SF); } - - return FoundSideEffects; } -bool swift::parseIntoSourceFile(SourceFile &SF, +void swift::parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL, PersistentParserState *PersistentState, bool DelayBodyParsing) { - return parseIntoSourceFileImpl(SF, BufferID, Done, SIL, - PersistentState, - /*FullParse=*/SF.shouldBuildSyntaxTree(), - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, Done, SIL, + PersistentState, + /*FullParse=*/SF.shouldBuildSyntaxTree(), + DelayBodyParsing); } -bool swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, +void swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState, bool DelayBodyParsing) { bool Done = false; - return parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, - PersistentState, /*FullParse=*/true, - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, + PersistentState, /*FullParse=*/true, + DelayBodyParsing); } @@ -228,7 +224,7 @@ namespace { /// Data structures used to perform name lookup of basic blocks. llvm::DenseMap BlocksByName; llvm::DenseMap> UndefinedBlocks; + Located> UndefinedBlocks; /// Data structures used to perform name lookup for local values. llvm::StringMap LocalValues; @@ -584,8 +580,8 @@ bool SILParser::diagnoseProblems() { if (!UndefinedBlocks.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto Entry : UndefinedBlocks) - P.diagnose(Entry.second.first, diag::sil_undefined_basicblock_use, - Entry.second.second); + P.diagnose(Entry.second.Loc, diag::sil_undefined_basicblock_use, + Entry.second.Item); HadError = true; } @@ -613,13 +609,13 @@ SILFunction *SILParser::getGlobalNameForDefinition(Identifier name, // complete the forward reference. auto iter = TUState.ForwardRefFns.find(name); if (iter != TUState.ForwardRefFns.end()) { - SILFunction *fn = iter->second.first; + SILFunction *fn = iter->second.Item; // Verify that the types match up. if (fn->getLoweredFunctionType() != ty) { P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), ty); - P.diagnose(iter->second.second, diag::sil_prior_reference); + P.diagnose(iter->second.Loc, diag::sil_prior_reference); fn = builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } @@ -717,7 +713,7 @@ SILBasicBlock *SILParser::getBBForReference(Identifier Name, SourceLoc Loc) { // Otherwise, create it and remember that this is a forward reference so // that we can diagnose use without definition problems. BB = F->createBasicBlock(); - UndefinedBlocks[BB] = {Loc, Name}; + UndefinedBlocks[BB] = {Name, Loc}; return BB; } @@ -2375,13 +2371,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - SmallVector, 4> resultNames; + SmallVector, 4> resultNames; SourceLoc resultClauseBegin; // If the instruction has a name '%foo =', parse it. if (P.Tok.is(tok::sil_local_name)) { resultClauseBegin = P.Tok.getLoc(); - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); P.consumeToken(tok::sil_local_name); // If the instruction has a '(%foo, %bar) = ', parse it. @@ -2395,7 +2391,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); P.consumeToken(tok::sil_local_name); if (P.consumeIf(tok::comma)) @@ -5084,7 +5080,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { results.size()); } else { for (size_t i : indices(results)) { - setLocalValue(results[i], resultNames[i].first, resultNames[i].second); + setLocalValue(results[i], resultNames[i].Item, resultNames[i].Loc); } } } diff --git a/lib/SIL/Linker.h b/lib/SIL/Linker.h index 12ad57ae57fff..2901959ddfbc6 100644 --- a/lib/SIL/Linker.h +++ b/lib/SIL/Linker.h @@ -33,10 +33,6 @@ class SILLinkerVisitor : public SILInstructionVisitor { /// Worklist of SILFunctions we are processing. llvm::SmallVector Worklist; - /// A list of callees of the current instruction being visited. cleared after - /// every instruction is visited. - llvm::SmallVector FunctionDeserializationWorklist; - /// The current linking mode. LinkingMode Mode; @@ -45,8 +41,7 @@ class SILLinkerVisitor : public SILInstructionVisitor { public: SILLinkerVisitor(SILModule &M, SILModule::LinkingMode LinkingMode) - : Mod(M), Worklist(), FunctionDeserializationWorklist(), - Mode(LinkingMode), Changed(false) {} + : Mod(M), Worklist(), Mode(LinkingMode), Changed(false) {} /// Process F, recursively deserializing any thing F may reference. /// Returns true if any deserialization was performed. diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index 9f9a79ae6fa77..120199e4fde5c 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -142,6 +142,18 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) #include "swift/AST/ReferenceStorage.def" #undef SHOULD_NEVER_VISIT_INST +/// Instructions that are interior pointers into a guaranteed value. +#define INTERIOR_POINTER_PROJECTION(INST) \ + OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ + INST##Inst *i) { \ + assert(i->getNumOperands() && "Expected to have non-zero operands"); \ + return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, \ + UseLifetimeConstraint::MustBeLive); \ + } +INTERIOR_POINTER_PROJECTION(RefElementAddr) +INTERIOR_POINTER_PROJECTION(RefTailAddr) +#undef INTERIOR_POINTER_PROJECTION + /// Instructions whose arguments are always compatible with one convention. #define CONSTANT_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ @@ -151,7 +163,6 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) ValueOwnershipKind::OWNERSHIP, \ UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ } -CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, AutoreleaseValue) @@ -271,7 +282,6 @@ ACCEPTS_ANY_OWNERSHIP_INST(BridgeObjectToWord) ACCEPTS_ANY_OWNERSHIP_INST(ClassifyBridgeObject) ACCEPTS_ANY_OWNERSHIP_INST(CopyBlock) ACCEPTS_ANY_OWNERSHIP_INST(OpenExistentialBox) -ACCEPTS_ANY_OWNERSHIP_INST(RefTailAddr) ACCEPTS_ANY_OWNERSHIP_INST(RefToRawPointer) ACCEPTS_ANY_OWNERSHIP_INST(SetDeallocating) ACCEPTS_ANY_OWNERSHIP_INST(ProjectExistentialBox) @@ -333,7 +343,6 @@ FORWARD_ANY_OWNERSHIP_INST(ConvertFunction) FORWARD_ANY_OWNERSHIP_INST(RefToBridgeObject) FORWARD_ANY_OWNERSHIP_INST(BridgeObjectToRef) FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast) -FORWARD_ANY_OWNERSHIP_INST(MarkUninitialized) FORWARD_ANY_OWNERSHIP_INST(UncheckedEnumData) FORWARD_ANY_OWNERSHIP_INST(DestructureStruct) FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) @@ -355,6 +364,8 @@ FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) } FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract) FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, StructExtract) +FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + MarkUninitialized) #undef CONSTANT_OR_NONE_OWNERSHIP_INST OperandOwnershipKindMap @@ -454,8 +465,8 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { // Otherwise, go through the ownership constraints of our successor arguments // and merge them. auto mergedKind = ValueOwnershipKind::merge(makeTransformRange( - sei->getSuccessorBlockArguments(), - [&](SILPhiArgumentArrayRef array) -> ValueOwnershipKind { + sei->getSuccessorBlockArgumentLists(), + [&](ArrayRef array) -> ValueOwnershipKind { // If the array is empty, we have a non-payloaded case. Return any. if (array.empty()) return ValueOwnershipKind::None; @@ -463,8 +474,7 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { // Otherwise, we should have a single element since a payload is // a tuple. assert(std::distance(array.begin(), array.end()) == 1); - SILPhiArgument *arg = array.front(); - return arg->getOwnershipKind(); + return array.front()->getOwnershipKind(); })); // If we failed to merge, return an empty map so we will fail to pattern match @@ -484,7 +494,7 @@ OperandOwnershipKindClassifier::visitCheckedCastBranchInst( CheckedCastBranchInst *ccbi) { // TODO: Simplify this using ValueOwnershipKind::merge. Optional map; - for (auto argArray : ccbi->getSuccessorBlockArguments()) { + for (auto argArray : ccbi->getSuccessorBlockArgumentLists()) { assert(!argArray.empty()); auto argOwnershipKind = argArray[getOperandIndex()]->getOwnershipKind(); diff --git a/lib/SIL/SILBuilder.cpp b/lib/SIL/SILBuilder.cpp index 907c3be1f22a7..a6a9bed4a3173 100644 --- a/lib/SIL/SILBuilder.cpp +++ b/lib/SIL/SILBuilder.cpp @@ -110,19 +110,6 @@ ProjectBoxInst *SILBuilder::createProjectBox(SILLocation Loc, getSILDebugLocation(Loc), boxOperand, index, fieldTy)); } -// If legal, create an unchecked_ref_cast from the given operand and result -// type, otherwise return null. -SingleValueInstruction * -SILBuilder::tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy) { - if (!SILType::canRefCast(Op->getType(), ResultTy, getModule())) - return nullptr; - - return insert(UncheckedRefCastInst::create(getSILDebugLocation(Loc), Op, - ResultTy, getFunction(), - C.OpenedArchetypes)); -} - ClassifyBridgeObjectInst * SILBuilder::createClassifyBridgeObject(SILLocation Loc, SILValue value) { auto &ctx = getASTContext(); @@ -142,8 +129,8 @@ SILBuilder::createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty) { return insert(UncheckedTrivialBitCastInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); - if (auto refCast = tryCreateUncheckedRefCast(Loc, Op, Ty)) - return refCast; + if (SILType::canRefCast(Op->getType(), Ty, getModule())) + return createUncheckedRefCast(Loc, Op, Ty); // The destination type is nontrivial, and may be smaller than the source // type, so RC identity cannot be assumed. diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index de5ec75efcad8..e5190d4abd128 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -2514,64 +2514,77 @@ TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, assert(base.requiresNewVTableEntry() && "base must not be an override"); + // Figure out the generic signature for the class method call. This is the + // signature of the derived class, with requirements transplanted from + // the base method. The derived method is allowed to have fewer + // requirements, in which case the thunk will translate the calling + // convention appropriately before calling the derived method. + bool hasGenericRequirementDifference = false; + + auto derivedSig = derived.getDecl()->getAsGenericContext() + ->getGenericSignature(); + auto genericSig = Context.getOverrideGenericSignature(base.getDecl(), + derived.getDecl()); + if (genericSig) { + hasGenericRequirementDifference = + !genericSig->requirementsNotSatisfiedBy(derivedSig).empty(); + } + auto baseInfo = getConstantInfo(context, base); auto derivedInfo = getConstantInfo(context, derived); - // If the derived method is ABI-compatible with the base method, give the - // vtable thunk the same signature as the derived method. - auto basePattern = AbstractionPattern(baseInfo.LoweredType); - - auto baseInterfaceTy = baseInfo.FormalType; - auto derivedInterfaceTy = derivedInfo.FormalType; - - auto params = derivedInterfaceTy.getParams(); + auto params = derivedInfo.FormalType.getParams(); assert(params.size() == 1); auto selfInterfaceTy = params[0].getPlainType()->getMetatypeInstanceType(); auto overrideInterfaceTy = + cast( selfInterfaceTy->adjustSuperclassMemberDeclType( - base.getDecl(), derived.getDecl(), baseInterfaceTy); - - // Copy generic signature from derived to the override type, to handle - // the case where the base member is not generic (because the base class - // is concrete) but the derived member is generic (because the derived - // class is generic). - if (auto derivedInterfaceFnTy = derivedInterfaceTy->getAs()) { - auto overrideInterfaceFnTy = overrideInterfaceTy->castTo(); - overrideInterfaceTy = - GenericFunctionType::get(derivedInterfaceFnTy->getGenericSignature(), - overrideInterfaceFnTy->getParams(), - overrideInterfaceFnTy->getResult(), - overrideInterfaceFnTy->getExtInfo()); - } + base.getDecl(), derived.getDecl(), baseInfo.FormalType) + ->getCanonicalType()); - // Lower the formal AST type. - auto bridgedTypes = getLoweredFormalTypes(derived, - cast(overrideInterfaceTy->getCanonicalType())); - auto overrideLoweredInterfaceTy = bridgedTypes.Uncurried; + // Build the formal AST function type for the class method call. + auto basePattern = AbstractionPattern(baseInfo.LoweredType); + + if (!hasGenericRequirementDifference && + !checkASTTypeForABIDifferences(derivedInfo.FormalType, + overrideInterfaceTy)) { - if (!checkASTTypeForABIDifferences(derivedInfo.LoweredType, - overrideLoweredInterfaceTy)) { + // The derived method is ABI-compatible with the base method. Let's + // just use the derived method's formal type. basePattern = AbstractionPattern( copyOptionalityFromDerivedToBase( *this, derivedInfo.LoweredType, baseInfo.LoweredType)); - overrideLoweredInterfaceTy = derivedInfo.LoweredType; + overrideInterfaceTy = derivedInfo.FormalType; + } + + if (genericSig && !genericSig->areAllParamsConcrete()) { + overrideInterfaceTy = + cast( + GenericFunctionType::get(genericSig, + overrideInterfaceTy->getParams(), + overrideInterfaceTy->getResult(), + overrideInterfaceTy->getExtInfo()) + ->getCanonicalType()); } - // Build the SILFunctionType for the vtable thunk. + // Build the lowered AST function type for the class method call. + auto bridgedTypes = getLoweredFormalTypes(derived, overrideInterfaceTy); + + // Build the SILFunctionType for the class method call. CanSILFunctionType fnTy = getNativeSILFunctionType( - *this, context, basePattern, overrideLoweredInterfaceTy, base, derived, + *this, context, basePattern, bridgedTypes.Uncurried, base, derived, /*reqt subs*/ None, ProtocolConformanceRef()); // Build the SILConstantInfo and cache it. auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), alignof(SILConstantInfo)); auto result = ::new (resultBuf) SILConstantInfo{ - derivedInterfaceTy, - bridgedTypes.Pattern, - overrideLoweredInterfaceTy, + overrideInterfaceTy, + basePattern, + bridgedTypes.Uncurried, fnTy}; auto inserted = ConstantOverrideTypes.insert({{derived, base}, result}); diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index bc92771412b21..27439f486b245 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -573,7 +573,7 @@ TryApplyInstBase::TryApplyInstBase(SILInstructionKind kind, SILDebugLocation loc, SILBasicBlock *normalBB, SILBasicBlock *errorBB) - : TermInst(kind, loc), DestBBs{{this, normalBB}, {this, errorBB}} {} + : TermInst(kind, loc), DestBBs{{{this, normalBB}, {this, errorBB}}} {} TryApplyInst::TryApplyInst( SILDebugLocation Loc, SILValue callee, SILType substCalleeTy, @@ -1217,13 +1217,13 @@ bool TermInst::isProgramTerminating() const { llvm_unreachable("Unhandled TermKind in switch."); } -TermInst::SuccessorBlockArgumentsListTy -TermInst::getSuccessorBlockArguments() const { - function_ref op; - op = [](const SILSuccessor &succ) -> SILPhiArgumentArrayRef { - return succ.getBB()->getSILPhiArguments(); +TermInst::SuccessorBlockArgumentListTy +TermInst::getSuccessorBlockArgumentLists() const { + function_ref(const SILSuccessor &)> op; + op = [](const SILSuccessor &succ) -> ArrayRef { + return succ.getBB()->getArguments(); }; - return SuccessorBlockArgumentsListTy(getSuccessors(), op); + return SuccessorBlockArgumentListTy(getSuccessors(), op); } YieldInst *YieldInst::create(SILDebugLocation loc, @@ -1267,8 +1267,7 @@ CondBranchInst::CondBranchInst(SILDebugLocation Loc, SILValue Condition, unsigned NumFalse, ProfileCounter TrueBBCount, ProfileCounter FalseBBCount) : InstructionBaseWithTrailingOperands(Condition, Args, Loc), - DestBBs{{this, TrueBB, TrueBBCount}, - {this, FalseBB, FalseBBCount}} { + DestBBs{{{this, TrueBB, TrueBBCount}, {this, FalseBB, FalseBBCount}}} { assert(Args.size() == (NumTrue + NumFalse) && "Invalid number of args"); SILInstruction::Bits.CondBranchInst.NumTrueArgs = NumTrue; assert(SILInstruction::Bits.CondBranchInst.NumTrueArgs == NumTrue && @@ -1706,7 +1705,7 @@ DynamicMethodBranchInst::DynamicMethodBranchInst(SILDebugLocation Loc, SILBasicBlock *NoMethodBB) : InstructionBase(Loc), Member(Member), - DestBBs{{this, HasMethodBB}, {this, NoMethodBB}}, + DestBBs{{{this, HasMethodBB}, {this, NoMethodBB}}}, Operands(this, Operand) { } diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 12579365809f1..d4c1dc09ec675 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -2206,10 +2206,6 @@ class SILVerifier : public SILVerifierBase { // outside by the allocating initializer and we pass in the to be // initialized value as a SILArgument. || isa(Src) - // FIXME: Once the MarkUninitializedFixup pass is eliminated, - // mark_uninitialized should never be applied to a project_box. So - // at that point, this should be eliminated. - || isa(Src) // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 13e8d03cca814..8f37adcb6b6d8 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -2172,6 +2172,20 @@ CaptureInfo TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { PrettyStackTraceSILLocation stack("getting lowered local captures", fn.getAsRegularLocation(), Context); + // If we're guaranteed to never have local captures, bail out now. + switch (fn.kind) { + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + return CaptureInfo::empty(); + + default: + if (fn.hasDecl()) { + if (!fn.getDecl()->isLocalCapture()) + return CaptureInfo::empty(); + } + + break; + } fn.isForeign = 0; fn.isCurried = 0; @@ -2199,8 +2213,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { std::function collectConstantCaptures; collectCaptures = [&](CaptureInfo captureInfo, DeclContext *dc) { - assert(captureInfo.hasBeenComputed() || - !TypeConverter::canCaptureFromParent(dc)); + assert(captureInfo.hasBeenComputed()); if (captureInfo.hasGenericParamCaptures()) capturesGenericParams = true; @@ -2325,6 +2338,9 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { }; collectFunctionCaptures = [&](AnyFunctionRef curFn) { + if (!curFn.getBody()) + return; + if (!visitedFunctions.insert(curFn).second) return; @@ -2609,10 +2625,23 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, return ABIDifference::NeedsThunk; } + // There is no ABI compatibility between non-throws and throws on WebAssembly, + // so need thunk. + if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { + if (!fnTy1->hasErrorResult() && fnTy2->hasErrorResult()) { + return ABIDifference::NeedsThunk; + } + } + auto rep1 = fnTy1->getRepresentation(), rep2 = fnTy2->getRepresentation(); if (rep1 != rep2) { if (rep1 == SILFunctionTypeRepresentation::Thin && rep2 == SILFunctionTypeRepresentation::Thick) { + // There is no ABI compatibility between thin and thick on WebAssembly, + // so need thunk. + if (M.getASTContext().LangOpts.Target.isOSBinFormatWasm()) { + return ABIDifference::NeedsThunk; + } if (DifferentFunctionTypesHaveDifferentRepresentation) { // FIXME: check whether the representations are compatible modulo // context @@ -2621,7 +2650,6 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M, return ABIDifference::CompatibleRepresentation_ThinToThick; } } - return ABIDifference::NeedsThunk; } diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 44fa9a81c4a16..94ebb2555fdd6 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -157,7 +157,8 @@ CONSTANT_OWNERSHIP_INST(Unowned, ValueToBridgeObject) #define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, INST) \ ValueOwnershipKind ValueOwnershipKindClassifier::visit##INST##Inst( \ INST##Inst *I) { \ - if (I->getType().isTrivial(*I->getFunction())) { \ + if (I->getType().isTrivial(*I->getFunction()) || \ + I->getType().isAddress()) { \ return ValueOwnershipKind::None; \ } \ return ValueOwnershipKind::OWNERSHIP; \ @@ -174,6 +175,12 @@ CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialValue) CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialBoxValue) CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, UnconditionalCheckedCastValue) +// Given an owned value, mark_uninitialized always forwards an owned value since +// we want to make sure that all destroys of that value must come through the +// mark_uninitialized (which will happen due to mark_uninitialized consuming the +// value). +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MarkUninitialized) + // unchecked_bitwise_cast is a bitwise copy. It produces a trivial or unowned // result. // @@ -249,7 +256,6 @@ FORWARDING_OWNERSHIP_INST(Tuple) FORWARDING_OWNERSHIP_INST(UncheckedRefCast) FORWARDING_OWNERSHIP_INST(UnconditionalCheckedCast) FORWARDING_OWNERSHIP_INST(Upcast) -FORWARDING_OWNERSHIP_INST(MarkUninitialized) FORWARDING_OWNERSHIP_INST(UncheckedEnumData) FORWARDING_OWNERSHIP_INST(SelectEnum) FORWARDING_OWNERSHIP_INST(Enum) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 242f3d32106c9..b87b9521dbffb 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1670,7 +1670,7 @@ class SourceFileScope { void SILGenModule::emitSourceFile(SourceFile *sf) { SourceFileScope scope(*this, sf); FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-file", sf); - for (Decl *D : sf->Decls) { + for (Decl *D : sf->getTopLevelDecls()) { FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-decl", D); visit(D); } diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index d5381bd9bea70..7815383d495b7 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -804,17 +804,6 @@ ManagedValue SILGenBuilder::createUncheckedAddrCast(SILLocation loc, ManagedValu return cloner.clone(cast); } -ManagedValue SILGenBuilder::tryCreateUncheckedRefCast(SILLocation loc, - ManagedValue original, - SILType type) { - CleanupCloner cloner(*this, original); - SILValue result = tryCreateUncheckedRefCast(loc, original.getValue(), type); - if (!result) - return ManagedValue(); - original.forward(SGF); - return cloner.clone(result); -} - ManagedValue SILGenBuilder::createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, SILType type) { diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index 2c391369774d0..c4b8cd3f1b42e 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -256,11 +256,6 @@ class SILGenBuilder : public SILBuilder { ManagedValue createUpcast(SILLocation loc, ManagedValue original, SILType type); - using SILBuilder::tryCreateUncheckedRefCast; - ManagedValue tryCreateUncheckedRefCast(SILLocation loc, - ManagedValue op, - SILType type); - using SILBuilder::createUncheckedTrivialBitCast; ManagedValue createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 31390a0b9838e..f1772f0e511df 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -649,13 +649,14 @@ emitBuiltinCastReference(SILGenFunction &SGF, auto &toTL = SGF.getTypeLowering(toTy); assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type"); + auto arg = args[0]; + // TODO: Fix this API. if (!fromTL.isAddress() || !toTL.isAddress()) { - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, args[0], - toTL.getLoweredType())) { + if (SILType::canRefCast(arg.getType(), toTL.getLoweredType(), SGF.SGM.M)) { // Create a reference cast, forwarding the cleanup. // The cast takes the source reference. - return refCast; + return SGF.B.createUncheckedRefCast(loc, arg, toTL.getLoweredType()); } } @@ -670,7 +671,7 @@ emitBuiltinCastReference(SILGenFunction &SGF, // TODO: For now, we leave invalid casts in address form so that the runtime // will trap. We could emit a noreturn call here instead which would provide // more information to the optimizer. - SILValue srcVal = args[0].ensurePlusOne(SGF, loc).forward(SGF); + SILValue srcVal = arg.ensurePlusOne(SGF, loc).forward(SGF); SILValue fromAddr; if (!fromTL.isAddress()) { // Move the loadable value into a "source temp". Since the source and @@ -745,17 +746,9 @@ static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, } // Create the appropriate bitcast based on the source and dest types. ManagedValue in = args[0]; - SILType resultTy = toTL.getLoweredType(); - if (resultTy.isTrivial(SGF.F)) - return SGF.B.createUncheckedTrivialBitCast(loc, in, resultTy); - // If we can perform a ref cast, just return. - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, in, resultTy)) - return refCast; - - // Otherwise leave the original cleanup and retain the cast value. - SILValue out = SGF.B.createUncheckedBitwiseCast(loc, in.getValue(), resultTy); - return SGF.emitManagedRetain(loc, out, toTL); + SILType resultTy = toTL.getLoweredType(); + return SGF.B.createUncheckedBitCast(loc, in, resultTy); } /// Specialized emitter for Builtin.castToBridgeObject. @@ -976,8 +969,8 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, SILType ElemType = SGF.getLoweredType(subs.getReplacementTypes()[1]-> getCanonicalType()).getObjectType(); - SILValue result = SGF.B.createRefTailAddr(loc, args[0].getValue(), - ElemType.getAddressType()); + SILValue result = SGF.B.createRefTailAddr( + loc, args[0].borrow(SGF, loc).getValue(), ElemType.getAddressType()); SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); result = SGF.B.createAddressToPointer(loc, result, rawPointerType); return ManagedValue::forUnmanaged(result); diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 0602dd7117830..fc5aac49dfbe5 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -827,12 +827,10 @@ ManagedValue SILGenFunction::emitExistentialErasure( loc, SILType::getPrimitiveObjectType(anyObjectTy), concreteFormalType, concreteValue, {}); }; - - auto concreteTLPtr = &concreteTL; + if (this->F.getLoweredFunctionType()->isPseudogeneric()) { if (anyObjectTy && concreteFormalType->is()) { concreteFormalType = anyObjectTy; - concreteTLPtr = &getTypeLowering(anyObjectTy); F = eraseToAnyObject; } } diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 3041fb58404ee..357bf94625f1f 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -186,7 +186,11 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, SILValue addr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, ti.getLoweredType().getAddressType()); + addr = B.createBeginAccess( + cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); B.createDestroyAddr(cleanupLoc, addr); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); } } } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 2548a1901de44..96ba74a233131 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -513,7 +513,12 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitStmt(ce->getBody()); } else { auto *autoclosure = cast(ace); - emitStmt(autoclosure->getBody()); + // Closure expressions implicitly return the result of their body + // expression. + if (B.hasValidInsertionPoint()) { + emitReturnExpr(ImplicitReturnLocation(ace), + autoclosure->getSingleExpressionBody()); + } } emitEpilog(ace); } @@ -538,7 +543,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { // be imported. ASTContext &ctx = getASTContext(); - std::pair UIKitName = + Located UIKitName = {ctx.getIdentifier("UIKit"), SourceLoc()}; ModuleDecl *UIKit = ctx @@ -695,31 +700,6 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { } } -#ifndef NDEBUG -/// If \c false, \c function is either a declaration that inherently cannot -/// capture variables, or it is in a context it cannot capture variables from. -/// In either case, it is expected that Sema may not have computed its -/// \c CaptureInfo. -/// -/// This call exists for use in assertions; do not use it to skip capture -/// processing. -static bool canCaptureFromParent(SILDeclRef function) { - switch (function.kind) { - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - return false; - - default: - if (function.hasDecl()) { - if (auto dc = dyn_cast(function.getDecl())) { - return TypeConverter::canCaptureFromParent(dc); - } - } - return false; - } -} -#endif - void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, bool EmitProfilerIncrement) { auto *dc = function.getDecl()->getInnermostDeclContext(); @@ -757,14 +737,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc()); } - CaptureInfo captureInfo; - if (function.getAnyFunctionRef()) - captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); - else { - assert(!canCaptureFromParent(function)); - captureInfo = CaptureInfo::empty(); - } - + auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); auto interfaceType = value->getType()->mapTypeOutOfContext(); emitProlog(captureInfo, params, /*selfParam=*/nullptr, dc, interfaceType, /*throws=*/false, SourceLoc()); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 747c6a97d1922..8b3b7e0e825db 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -739,13 +739,16 @@ namespace { SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(), Field, SubstFieldType); - // Avoid emitting access markers completely for non-accesses or immutable - // declarations. Access marker verification is aware of these cases. - if (!IsNonAccessing && !Field->isLet()) { - if (auto enforcement = SGF.getDynamicEnforcement(Field)) { - result = enterAccessScope(SGF, loc, result, getTypeData(), - getAccessKind(), *enforcement); - } + // Avoid emitting non-trivial access markers for non-accesses and + // immutable values. + auto enforcement = SGF.getDynamicEnforcement(Field); + if (enforcement && !IsNonAccessing && !Field->isLet()) { + result = enterAccessScope(SGF, loc, result, getTypeData(), + getAccessKind(), *enforcement); + } else { + result = + enterAccessScope(SGF, loc, result, getTypeData(), getAccessKind(), + SILAccessEnforcement::Unsafe); } return ManagedValue::forLValue(result); diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 3b95cc66d571e..711b70d101178 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -311,7 +311,7 @@ namespace { /// A row which we intend to specialize. struct RowToSpecialize { /// The pattern from this row which we are specializing upon. - Pattern *Pattern; + swift::Pattern *Pattern; /// The index of the target row. unsigned RowIndex; diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 017415a368032..ed8b7fd0d9fd5 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3602,10 +3602,10 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, SGM.Types.getConstantInfo(getTypeExpansionContext(), derived).SILFnType; } - SubstitutionMap subs; - if (auto *genericEnv = fd->getGenericEnvironment()) { - F.setGenericEnvironment(genericEnv); - subs = getForwardingSubstitutionMap(); + auto subs = getForwardingSubstitutionMap(); + if (auto genericSig = derivedFTy->getSubstGenericSignature()) { + subs = SubstitutionMap::get(genericSig, subs); + derivedFTy = derivedFTy->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 5b39eec77c341..e4d94aae962c4 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -445,8 +445,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo, // Emit the capture argument variables. These are placed last because they // become the first curry level of the SIL function. - assert((captureInfo.hasBeenComputed() || - !TypeConverter::canCaptureFromParent(DC)) && + assert(captureInfo.hasBeenComputed() && "can't emit prolog of function with uncomputed captures"); for (auto capture : captureInfo.getCaptures()) { if (capture.isDynamicSelfMetadata()) { diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 78dfdba0cfb85..8cb86b426b4cb 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -482,6 +482,7 @@ void SILGenFunction::emitReturnExpr(SILLocation branchLoc, RValue RV = emitRValue(ret).ensurePlusOne(*this, CleanupLocation(ret)); std::move(RV).forwardAll(*this, directResults); } + Cleanups.emitBranchAndCleanups(ReturnDest, branchLoc, directResults); } @@ -902,14 +903,28 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { } void StmtEmitter::visitForEachStmt(ForEachStmt *S) { + // Dig out information about the sequence conformance. + auto sequenceConformance = S->getSequenceConformance(); + Type sequenceType = S->getSequence()->getType(); + auto sequenceProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); + auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( + sequenceProto, sequenceType, sequenceConformance); + // Emit the 'iterator' variable that we'll be using for iteration. LexicalScope OuterForScope(SGF, CleanupLocation(S)); { auto initialization = SGF.emitInitializationForVarDecl(S->getIteratorVar(), false); SILLocation loc = SILLocation(S->getSequence()); + + // Compute the reference to the Sequence's makeIterator(). + FuncDecl *makeIteratorReq = SGF.getASTContext().getSequenceMakeIterator(); + ConcreteDeclRef makeIteratorRef(makeIteratorReq, sequenceSubs); + + // Call makeIterator(). RValue result = SGF.emitApplyMethod( - loc, S->getMakeIterator(), ArgumentSource(S->getSequence()), + loc, makeIteratorRef, ArgumentSource(S->getSequence()), PreparedArguments(ArrayRef({})), SGFContext(initialization.get())); if (!result.isInContext()) { @@ -951,8 +966,26 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { JumpDest endDest = createJumpDest(S->getBody()); SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); + // Compute the reference to the the iterator's next(). + auto iteratorProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::IteratorProtocol); + ValueDecl *iteratorNextReq = iteratorProto->getSingleRequirement( + DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, + ArrayRef())); + auto iteratorAssocType = + sequenceProto->getAssociatedType(SGF.getASTContext().Id_Iterator); + auto iteratorMemberRef = DependentMemberType::get( + sequenceProto->getSelfInterfaceType(), iteratorAssocType); + auto iteratorType = sequenceConformance.getAssociatedType( + sequenceType, iteratorMemberRef); + auto iteratorConformance = sequenceConformance.getAssociatedConformance( + sequenceType, iteratorMemberRef, iteratorProto); + auto iteratorSubs = SubstitutionMap::getProtocolSubstitutions( + iteratorProto, iteratorType, iteratorConformance); + ConcreteDeclRef iteratorNextRef(iteratorNextReq, iteratorSubs); + auto buildArgumentSource = [&]() { - if (cast(S->getIteratorNext().getDecl())->getSelfAccessKind() == + if (cast(iteratorNextRef.getDecl())->getSelfAccessKind() == SelfAccessKind::Mutating) { LValue lv = SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::ReadWrite); @@ -968,7 +1001,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) { RValue result; result = SGF.emitApplyMethod( - loc, S->getIteratorNext(), buildArgumentSource(), + loc, iteratorNextRef, buildArgumentSource(), PreparedArguments(ArrayRef({})), S->getElementExpr() ? SGFContext() : ctx); if (S->getElementExpr()) { diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 450c98beca5e5..e0c75ea2a0ab6 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -165,12 +165,16 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, if (auto existingThunk = M.lookUpFunction(name)) return SILVTable::Entry(base, existingThunk, implKind); + GenericEnvironment *genericEnv = nullptr; + if (auto genericSig = overrideInfo.FormalType.getOptGenericSignature()) + genericEnv = genericSig->getGenericEnvironment(); + // Emit the thunk. SILLocation loc(derivedDecl); SILGenFunctionBuilder builder(*this); auto thunk = builder.createFunction( SILLinkage::Private, name, overrideInfo.SILFnType, - cast(derivedDecl)->getGenericEnvironment(), loc, + genericEnv, loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, ProfileCounter(), IsThunk); thunk->setDebugScope(new (M) SILDebugScope(loc, thunk)); diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 90e304d0a5084..8dece001a856a 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -678,7 +678,7 @@ bool AliasAnalysis::canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr) { if (ArgEffect.mayRelease()) { // The function may release this argument, so check if the pointer can // escape to it. - if (EA->canEscapeToValue(Ptr, FAS.getArgument(Idx))) + if (EA->mayReleaseContent(FAS.getArgument(Idx), Ptr)) return true; } } @@ -696,54 +696,51 @@ bool AliasAnalysis::canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr) { // A builtin can only release an object if it can escape to one of the // builtin's arguments. - if (EA->canEscapeToValue(Ptr, Arg)) + if (EA->mayReleaseContent(Arg, Ptr)) return true; } return false; } +// If the deinit for releasedReference can release any values used by User, then +// this is an interference. (The retains that originally forced liveness of +// those values may have already been eliminated). Note that we only care about +// avoiding a dangling pointer. The memory side affects of Release are +// unordered. +// +// \p releasedReference must be a value that directly contains the references +// being released. It cannot be an address or other kind of pointer that +// indirectly releases a reference. Otherwise, the escape analysis query is +// invalid. +bool AliasAnalysis::mayValueReleaseInterfereWithInstruction( + SILInstruction *User, SILValue releasedReference) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); -bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr) { - // TODO: Its important to make this as precise as possible. - // - // TODO: Eventually we can plug in some analysis on the what the release of - // the Ptr can do, i.e. be more precise about Ptr's deinit. - // - // TODO: If we know the specific release instruction, we can potentially do - // more. - // // If this instruction can not read or write any memory. Its OK. if (!User->mayReadOrWriteMemory()) return false; - // These instructions do read or write memory, get memory directly - // accessed. 'V' must be the only memory accessed by User, and it must be - // directly accessed. Any memory indirectly accessed via 'User' may have - // escaped. - SILValue V = getDirectlyAccessedMemory(User); - if (!V) - return true; - - // If the 'User' instruction's memory is uniquely identified and does not - // escape in the local scope, then it can't be accessed by a deinit in the - // local scope. Note that an exclusive argument's content may have escaped in - // the caller, but the argument value itself can't be accessed via aliasing - // references and we know that User doesn't see through any indirection. - if (!isUniquelyIdentified(V)) + // Get a pointer to the memory directly accessed by 'Users' (either via an + // address or heap reference operand). If additional memory may be indirectly + // accessed by 'User', such as via an inout argument, then stop here because + // mayReleaseContent can only reason about one level of memory access. + // + // TODO: Handle @inout arguments by iterating over the apply arguments. For + // each argument find out if any reachable content can be released. This is + // slightly more involved than mayReleaseContent because it needs to check all + // connection graph nodes reachable from accessedPointer that don't pass + // through another stored reference. + SILValue accessedPointer = getDirectlyAccessedMemory(User); + if (!accessedPointer) return true; - // This is a scoped allocation. - // The most important check: does the object escape the current function? - auto LO = getUnderlyingObject(V); - auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Node = ConGraph->getNodeOrNull(LO); - if (Node && !Node->escapes()) - return false; - - // This is either a non-local allocation or a scoped allocation that escapes. - // We failed to prove anything, it could be read or written by the deinit. - return true; + // If releasedReference can reach the first refcounted object reachable from + // accessedPointer, then releasing it early may destroy the object accessed by + // accessedPointer. Access to any objects beyond the first released refcounted + // object are irrelevant--they must already have sufficient refcount that they + // won't be released when releasing Ptr. + return EA->mayReleaseContent(releasedReference, accessedPointer); } bool swift::isLetPointer(SILValue V) { diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 99e3fae1dbf49..ecadc10197eb4 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -30,127 +30,91 @@ static llvm::cl::opt EnableInternalVerify( llvm::cl::desc("Enable internal verification of escape analysis"), llvm::cl::init(false)); -// Returns true if \p Ty recursively contains a reference. If \p mustBeRef is -// true, only return true if the type is guaranteed to hold a reference. If \p -// mustBeRef is false, only return false if the type is guaranteed not to hold a -// reference. -// -// If \p Ty is itself an address, return false. -static bool findRecursiveRefType(SILType Ty, const SILFunction &F, - bool mustBeRef) { - if (mustBeRef) { - // An address *may* be converted into a reference via something like - // raw_pointer_to_ref. However, addresses don't normally refer to the head - // of a reference counted object. - // - // The check for trivial types catches types that have AST "reference - // semantics", but are determined by type lowering to be trivial, such as - // noescape function types. - if (Ty.isAddress() || Ty.isTrivial(F)) - return false; - } +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findRecursivePointerKind(SILType Ty, + const SILFunction &F) const { + // An address may be converted into a reference via something like + // raw_pointer_to_ref, but in general we don't know what kind of pointer it + // is. + if (Ty.isAddress()) + return EscapeAnalysis::AnyPointer; - if (!mustBeRef) { - // Opaque types may contain a reference. Speculatively track them too. - // - // 1. It may be possible to optimize opaque values based on known mutation - // points. - // - // 2. A specialized function may call a generic function passing a concrete - // reference type via incomplete specialization. - // - // 3. A generic function may call a specialized function taking a concrete - // reference type via devirtualization. - if (Ty.isAddressOnly(F)) - return true; + // Opaque types may contain a reference. Speculatively track them too. + // + // 1. It may be possible to optimize opaque values based on known mutation + // points. + // + // 2. A specialized function may call a generic function passing a concrete + // reference type via incomplete specialization. + // + // 3. A generic function may call a specialized function taking a concrete + // reference type via devirtualization. + if (Ty.isAddressOnly(F)) + return EscapeAnalysis::AnyPointer; - if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) - return true; - } + // A raw pointer definitely does not have a reference, but could point + // anywhere. We do track these because critical stdlib data structures often + // use raw pointers under the hood. + if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) + return EscapeAnalysis::AnyPointer; if (Ty.hasReferenceSemantics()) - return true; + return EscapeAnalysis::ReferenceOnly; - auto &Mod = F.getModule(); + auto &M = F.getModule(); + // Start with the most precise pointer kind + PointerKind aggregateKind = NoPointer; + auto meetAggregateKind = [&](PointerKind otherKind) { + if (otherKind > aggregateKind) + aggregateKind = otherKind; + }; if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - if (findRecursiveRefType( - Ty.getFieldType(Field, Mod, F.getTypeExpansionContext()), F, - mustBeRef)) - return true; + SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()) + .getObjectType(); + meetAggregateKind(findCachedPointerKind(fieldTy, F)); } - return false; + return aggregateKind; } if (auto TT = Ty.getAs()) { for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { - if (findRecursiveRefType(Ty.getTupleElementType(i), F, mustBeRef)) - return true; + meetAggregateKind(findCachedPointerKind(Ty.getTupleElementType(i), F)); } - return false; + return aggregateKind; } if (auto En = Ty.getEnumOrBoundGenericEnum()) { for (auto *ElemDecl : En->getAllElements()) { - if (ElemDecl->hasAssociatedValues() && - findRecursiveRefType( - Ty.getEnumElementType(ElemDecl, Mod, F.getTypeExpansionContext()), - F, mustBeRef)) - return true; + if (!ElemDecl->hasAssociatedValues()) + continue; + SILType eltTy = + Ty.getEnumElementType(ElemDecl, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(eltTy, F)); } - return false; + return aggregateKind; } - // FIXME: without a covered switch, this is not robust for mayContainReference - // in the event that new reference-holding AST types are invented. - return false; + // FIXME: without a covered switch, this is not robust in the event that new + // reference-holding AST types are invented. + return NoPointer; } -// Returns true if the type \p Ty is a reference or may transitively contain -// a reference. If \p Ty is itself an address, return false. -// -// An address may contain a reference because addresses can be cast into -// reference types. -static bool mayContainReference(SILType Ty, const SILFunction &F) { - if (Ty.isAddress()) - return true; - return findRecursiveRefType(Ty, F, false); -} +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { + auto iter = pointerKindCache.find(Ty); + if (iter != pointerKindCache.end()) + return iter->second; -// Returns true if the type \p Ty must be a reference or must transitively -// contain a reference. If \p Ty is itself an address, return false. -static bool mustContainReference(SILType Ty, const SILFunction &F) { - return findRecursiveRefType(Ty, F, true); -} - -bool EscapeAnalysis::isPointer(ValueBase *V) const { - auto *F = V->getFunction(); - - // The function can be null, e.g. if V is an undef. - if (!F) - return false; - - SILType Ty = V->getType(); - auto Iter = isPointerCache.find(Ty); - if (Iter != isPointerCache.end()) - return Iter->second; - - bool IP = mayContainReference(Ty, *F); - const_cast(this)->isPointerCache[Ty] = IP; - return IP; -} - -static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (TEI->getFieldNo() == 1) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; - } - return false; + PointerKind pointerKind = findRecursivePointerKind(Ty, F); + const_cast(this)->pointerKindCache[Ty] = pointerKind; + return pointerKind; } // If EscapeAnalysis should consider the given value to be a derived address or // pointer based on one of its address or pointer operands, then return that // operand value. Otherwise, return an invalid value. -SILValue EscapeAnalysis::getPointerBase(SILValue value) const { +SILValue EscapeAnalysis::getPointerBase(SILValue value) { switch (value->getKind()) { case ValueKind::IndexAddrInst: case ValueKind::IndexRawPointerInst: @@ -185,10 +149,8 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const { case ValueKind::TupleExtractInst: { auto *TEI = cast(value); // Special handling for extracting the pointer-result from an - // array construction. We handle this like a ref_element_addr - // rather than a projection. See the handling of tuple_extract - // in analyzeInstruction(). - if (isExtractOfArrayUninitializedPointer(TEI)) + // array construction. See createArrayUninitializedSubgraph. + if (canOptimizeArrayUninitializedResult(TEI)) return SILValue(); return TEI->getOperand(); } @@ -217,7 +179,7 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const { // Recursively find the given value's pointer base. If the value cannot be // represented in EscapeAnalysis as one of its operands, then return the same // value. -SILValue EscapeAnalysis::getPointerRoot(SILValue value) const { +SILValue EscapeAnalysis::getPointerRoot(SILValue value) { while (true) { if (SILValue v2 = getPointerBase(value)) value = v2; @@ -362,15 +324,26 @@ EscapeAnalysis::CGNode::RepValue EscapeAnalysis::CGNode::getRepValue() const { depth}; } +void EscapeAnalysis::CGNode::mergeFlags(bool isInterior, + bool hasReferenceOnly) { + // isInterior is conservatively preserved from either node unless two content + // nodes are being merged and one is the interior node's content. + isInteriorFlag |= isInterior; + + // hasReferenceOnly is always conservatively merged. + hasReferenceOnlyFlag &= hasReferenceOnly; +} + void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { - // TODO: Optimistically merge hasRC. 'this' node can only be merged with - // `fromNode` if their pointer values are compatible. If `fromNode->hasRC` is - // true, then it is guaranteed to represent the head of a heap object. Thus, - // it can only be merged with 'this' when the pointer values that access - // 'this' are also references. - // - // For now, this is pessimistic until we understand performance implications. - hasRC &= fromNode->hasRC; + // isInterior is conservatively preserved from either node unless the other + // node is the interior node's content. + bool isInterior = fromNode->isInteriorFlag; + if (fromNode == pointsTo) + this->isInteriorFlag = isInterior; + else if (this == fromNode->pointsTo) + isInterior = this->isInteriorFlag; + + mergeFlags(isInterior, fromNode->hasReferenceOnlyFlag); } template @@ -413,38 +386,68 @@ void EscapeAnalysis::ConnectionGraph::clear() { } EscapeAnalysis::CGNode * -EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { +EscapeAnalysis::ConnectionGraph::getOrCreateNode(ValueBase *V, + PointerKind pointerKind) { + assert(pointerKind != EscapeAnalysis::NoPointer); + if (isa(V) || isa(V) || isa(V)) return nullptr; - // In the case of a struct or tuple extract, 'V' may not be a pointer - // even if it's pointer root is a pointer. Bail first because we only expect - // graph nodes for pointer values. - if (!EA->isPointer(V)) - return nullptr; - - // Look past address projections, pointer casts, and the like within the same - // object. Does not look past a dereference such as ref_element_addr, or - // project_box. - V = EA->getPointerRoot(V); - - if (!createIfNeeded) - return lookupNode(V); - CGNode * &Node = Values2Nodes[V]; + // Nodes mapped to values must have an indirect pointsTo. Nodes that don't + // have an indirect pointsTo are imaginary nodes that don't directly represnt + // a SIL value. + bool hasReferenceOnly = canOnlyContainReferences(pointerKind); if (!Node) { if (isa(V)) { - Node = allocNode(V, NodeType::Argument); + Node = allocNode(V, NodeType::Argument, false, hasReferenceOnly); if (!isSummaryGraph) Node->mergeEscapeState(EscapeState::Arguments); } else { - Node = allocNode(V, NodeType::Value); + Node = allocNode(V, NodeType::Value, false, hasReferenceOnly); } } return Node->getMergeTarget(); } +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { + PointerKind pointerKind = EA->getPointerKind(V); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + V = EA->getPointerRoot(V); + + if (!createIfNeeded) + return lookupNode(V); + + return getOrCreateNode(V, pointerKind); +} + +/// Adds an argument/instruction in which the node's memory is released. +int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, + SILInstruction *User) { + // Use points are never consulted for escaping nodes, but still need to + // propagate to other nodes in a defer web. Even if this node is escaping, + // some defer predecessors may not be escaping. Only checking if this node has + // defer predecessors is insufficient because a defer successor of this node + // may have defer predecessors. + if (Node->getEscapeState() >= EscapeState::Global) + return -1; + + int Idx = (int)UsePoints.size(); + assert(UsePoints.count(User) == 0 && "value is already a use-point"); + UsePoints[User] = Idx; + UsePointTable.push_back(User); + assert(UsePoints.size() == UsePointTable.size()); + Node->setUsePointBit(Idx); + return Idx; +} + CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, bool &Changed) { if (!From->canAddDeferred(To)) @@ -640,9 +643,9 @@ void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { assert(To->pointsTo != From); } else { // If 'To' has no pointsTo at all, initialize its defer web. - if (!To->pointsTo) { + if (!To->pointsTo) initializePointsToEdge(To, redirectPointsTo(From->pointsTo)); - } else { + else { // Upgrade 'To's pointsTo to an edge to preserve the fact that 'From' // had a pointsTo edge. To->pointsToIsEdge = true; @@ -792,10 +795,14 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the state to all successor nodes. + // Propagate the state to all pointsTo nodes. It would be sufficient to + // only follow proper pointsTo edges, since this loop also follows defer + // edges, but this may converge faster. if (Node->pointsTo) { Changed |= Node->pointsTo->mergeEscapeState(Node->State); } + // Note: Propagating along defer edges may be interesting from an SSA + // standpoint, but it is entirely irrelevant alias analysis. for (CGNode *Def : Node->defersTo) { Changed |= Def->mergeEscapeState(Node->State); } @@ -810,15 +817,6 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { #endif // First scan the whole function and add relevant instructions as use-points. for (auto &BB : *F) { - for (SILArgument *BBArg : BB.getArguments()) { - /// In addition to releasing instructions (see below) we also add block - /// arguments as use points. In case of loops, block arguments can - /// "extend" the liferange of a reference in upward direction. - if (CGNode *ArgNode = lookupNode(BBArg)) { - addUsePoint(ArgNode, BBArg); - } - } - for (auto &I : BB) { switch (I.getKind()) { #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ @@ -833,14 +831,13 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { /// liferange. And that must be a releasing instruction. int ValueIdx = -1; for (const Operand &Op : I.getAllOperands()) { - ValueBase *OpV = Op.get(); - if (CGNode *OpNd = lookupNode(EA->getPointerRoot(OpV))) { - if (ValueIdx < 0) { - ValueIdx = addUsePoint(OpNd, &I); - } else { - OpNd->setUsePointBit(ValueIdx); - } - } + CGNode *content = getValueContent(Op.get()); + if (!content) + continue; + if (ValueIdx < 0) + ValueIdx = addUsePoint(content, &I); + else + content->setUsePointBit(ValueIdx); } break; } @@ -855,24 +852,38 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { do { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the bits to all successor nodes. - Node->visitSuccessors([&Changed, Node](CGNode *succ) { - Changed |= succ->mergeUsePoints(Node); - return true; - }); + // Propagate the bits to pointsTo. A release of a node may also release + // any content pointed to be the node. + if (Node->pointsTo) + Changed |= Node->pointsTo->mergeUsePoints(Node); } } while (Changed); } -CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, - bool hasRC) { - CGNode *newContent = allocNode(nullptr, NodeType::Content, hasRC); +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + CGNode *newContent = + allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly); initializePointsToEdge(addrNode, newContent); assert(ToMerge.empty() && "Initially setting pointsTo should not require any node merges"); return newContent; } +CGNode *EscapeAnalysis::ConnectionGraph::getOrCreateContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + if (CGNode *content = addrNode->getContentNodeOrNull()) { + content->mergeFlags(isInterior, hasReferenceOnly); + return content; + } + CGNode *content = createContentNode(addrNode, isInterior, hasReferenceOnly); + // getValueContent may be called after the graph is built and escape states + // are propagated. Keep the escape state and use points consistent here. + content->mergeEscapeState(addrNode->State); + content->mergeUsePoints(addrNode); + return content; +} + // Create a content node for merging based on an address node in the destination // graph and a content node in the source graph. CGNode * @@ -881,19 +892,105 @@ EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, // destAddrNode may itself be a content node, so its value may be null. Since // we don't have the original pointer value, build a new content node based // on the source content. - return createContentNode(destAddrNode, srcContent->hasRC); + CGNode *mergedContent = createContentNode( + destAddrNode, srcContent->isInterior(), srcContent->hasReferenceOnly()); + return mergedContent; +} + +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateAddressContent(SILValue addrVal, + CGNode *addrNode) { + assert(addrVal->getType().isAddress()); + + bool contentHasReferenceOnly = + EA->hasReferenceOnly(addrVal->getType().getObjectType(), *F); + // Address content always has an indirect pointsTo (only reference content can + // have a non-indirect pointsTo). + return getOrCreateContentNode(addrNode, false, contentHasReferenceOnly); +} + +// refVal is allowed to be invalid so we can model escaping content for +// secondary deinitializers of released objects. +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, + CGNode *refNode) { + // The object node points to internal fields. It neither has indirect pointsTo + // nor reference-only pointsTo. + CGNode *objNode = getOrCreateContentNode(refNode, true, false); + if (!objNode->isInterior()) + return objNode; + + bool contentHasReferenceOnly = false; + if (refVal) { + SILType refType = refVal->getType(); + if (auto *C = refType.getClassOrBoundGenericClass()) { + PointerKind aggregateKind = NoPointer; + for (auto *field : C->getStoredProperties()) { + SILType fieldType = refType + .getFieldType(field, F->getModule(), + F->getTypeExpansionContext()) + .getObjectType(); + PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); + if (fieldKind > aggregateKind) + aggregateKind = fieldKind; + } + contentHasReferenceOnly = canOnlyContainReferences(aggregateKind); + } + } + getOrCreateContentNode(objNode, false, contentHasReferenceOnly); + return objNode; } -// Get a node representing the field data within the given reference-counted -// node. The caller has already determined that rcNode represents the head of a -// heap object rather than field content or the address of a local variable or -// argument. -CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { - assert(rcNode->isContent()); - if (rcNode->pointsTo) - return rcNode->pointsTo; +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateUnknownContent(CGNode *addrNode) { + // We don't know if addrVal has been cast from a reference or raw + // pointer. More importantly, we don't know what memory contents it may + // point to. There's no need to consider it an "interior" node initially. If + // it's ever merged with another interior node (from ref_element_addr), then + // it will conservatively take on the interior flag at that time. + return getOrCreateContentNode(addrNode, false, false); +} + +// If ptrVal is itself mapped to a node, then this must return a non-null +// contentnode. Otherwise, setEscapesGlobal won't be able to represent escaping +// memory. +// +// This may be called after the graph is built and all escape states and use +// points are propagate. If a new content node is created, update its state +// on-the-fly. +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getValueContent(SILValue ptrVal) { + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + SILValue ptrBase = EA->getPointerRoot(ptrVal); + + PointerKind pointerKind = EA->getPointerKind(ptrBase); + if (pointerKind == EscapeAnalysis::NoPointer) + return nullptr; + + CGNode *addrNode = getOrCreateNode(ptrBase, pointerKind); + if (!addrNode) + return nullptr; + + if (ptrBase->getType().isAddress()) + return getOrCreateAddressContent(ptrBase, addrNode); + + if (canOnlyContainReferences(pointerKind)) + return getOrCreateReferenceContent(ptrBase, addrNode); + + // The pointer value may contain raw pointers. + return getOrCreateUnknownContent(addrNode); +} - return createContentNode(rcNode, /*hasRC=*/false); +CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() { + if (!ReturnNode) { + SILType resultTy = + F->mapTypeIntoContext(F->getConventions().getSILResultType()); + bool hasReferenceOnly = EA->hasReferenceOnly(resultTy, *F); + ReturnNode = allocNode(nullptr, NodeType::Return, false, hasReferenceOnly); + } + return ReturnNode; } bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, @@ -918,7 +1015,9 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // global escaping state set. // Just set global escaping in the caller node and that's it. Changed |= DestNd->mergeEscapeState(EscapeState::Global); - continue; + // If DestNd is an interior node, its content still needs to be created. + if (!DestNd->isInterior()) + continue; } CGNode *SourcePT = SourceNd->pointsTo; @@ -984,11 +1083,10 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, /// somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. -bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, +bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, CGNode *Node) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); - UsePoint = UsePoint->getRepresentativeSILNodeInObject(); auto Iter = UsePoints.find(UsePoint); if (Iter == UsePoints.end()) return false; @@ -998,8 +1096,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, return Node->UsePoints.test(Idx); } -void EscapeAnalysis::ConnectionGraph:: -getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { +void EscapeAnalysis::ConnectionGraph::getUsePoints( + CGNode *Node, llvm::SmallVectorImpl &UsePoints) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); for (int Idx = Node->UsePoints.find_first(); Idx >= 0; @@ -1066,19 +1164,6 @@ bool EscapeAnalysis::ConnectionGraph::forwardTraverseDefer( return true; } -bool EscapeAnalysis::ConnectionGraph::mayReach(CGNode *pointer, - CGNode *pointee) { - if (pointer == pointee) - return true; - - // This query is successful when the traversal halts and returns false. - return !backwardTraverse(pointee, [pointer](Predecessor pred) { - if (pred.getPredNode() == pointer) - return Traversal::Halt; - return Traversal::Follow; - }); -} - void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { CGNode *node = Values2Nodes.lookup(V); if (!node) @@ -1098,10 +1183,7 @@ void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { /// This makes iterating over the edges easier. struct CGForDotView { - enum EdgeTypes { - PointsTo, - Deferred - }; + enum EdgeTypes { PointsTo, Reference, Deferred }; struct Node { EscapeAnalysis::CGNode *OrigNode; @@ -1153,7 +1235,10 @@ CGForDotView::CGForDotView(const EscapeAnalysis::ConnectionGraph *CG) : Nd.OrigNode = OrigNode; if (auto *PT = OrigNode->getPointsToEdge()) { Nd.Children.push_back(Orig2Node[PT]); - Nd.ChildrenTypes.push_back(PointsTo); + if (OrigNode->hasReferenceOnly()) + Nd.ChildrenTypes.push_back(Reference); + else + Nd.ChildrenTypes.push_back(PointsTo); } for (auto *Def : OrigNode->defersTo) { Nd.Children.push_back(Orig2Node[Def]); @@ -1208,7 +1293,7 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { switch (Orig->Type) { case EscapeAnalysis::NodeType::Content: attr = "style=\"rounded"; - if (Orig->hasRefCount()) { + if (Orig->isInterior()) { attr += ",filled"; } attr += "\""; @@ -1301,8 +1386,12 @@ namespace llvm { const CGForDotView *Graph) { unsigned ChildIdx = I - Node->Children.begin(); switch (Node->ChildrenTypes[ChildIdx]) { - case CGForDotView::PointsTo: return ""; - case CGForDotView::Deferred: return "color=\"gray\""; + case CGForDotView::PointsTo: + return ""; + case CGForDotView::Reference: + return "color=\"green\""; + case CGForDotView::Deferred: + return "color=\"gray\""; } llvm_unreachable("Unhandled CGForDotView in switch."); @@ -1330,8 +1419,10 @@ void EscapeAnalysis::ConnectionGraph::dumpCG() const { void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); - if (hasRefCount()) - llvm::errs() << " [rc]"; + if (isInterior()) + llvm::errs() << " [int]"; + if (hasReferenceOnly()) + llvm::errs() << " [ref]"; auto rep = getRepValue(); if (rep.depth > 0) @@ -1391,16 +1482,6 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { }); }; - auto nodeStr = [&](CGNode *Nd) -> std::string { - std::string Str; - llvm::raw_string_ostream OS(Str); - if (Nd->hasRefCount()) - OS << "[rc] "; - Nd->getRepValue().print(OS, InstToIDMap); - OS.flush(); - return Str; - }; - llvm::SmallVector SortedNodes; for (CGNode *Nd : Nodes) { if (!Nd->isMerged) @@ -1409,7 +1490,13 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { sortNodes(SortedNodes); for (CGNode *Nd : SortedNodes) { - OS << " " << Nd->getTypeStr() << ' ' << nodeStr(Nd) << " Esc: "; + OS << " " << Nd->getTypeStr() << ' '; + if (Nd->isInterior()) + OS << "[int] "; + if (Nd->hasReferenceOnly()) + OS << "[ref] "; + Nd->getRepValue().print(OS, InstToIDMap); + OS << " Esc: "; switch (Nd->getEscapeState()) { case EscapeState::None: { const char *Separator = ""; @@ -1434,13 +1521,16 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << ", Succ: "; const char *Separator = ""; if (CGNode *PT = Nd->getPointsToEdge()) { - OS << '(' << nodeStr(PT) << ')'; + OS << '('; + PT->getRepValue().print(OS, InstToIDMap); + OS << ')'; Separator = ", "; } llvm::SmallVector SortedDefers = Nd->defersTo; sortNodes(SortedDefers); for (CGNode *Def : SortedDefers) { - OS << Separator << nodeStr(Def); + OS << Separator; + Def->getRepValue().print(OS, InstToIDMap); Separator = ", "; } OS << '\n'; @@ -1476,10 +1566,11 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { // which consist of only defer-edges and a single trailing points-to edge // must lead to the same assert(Nd->matchPointToOfDefers(allowMerge)); - if (Nd->hasRefCount()) { - SILValue v = Nd->getRepValue().getValue(); - (void)v; - assert(!v || mayContainReference(v->getType(), *F)); + if (Nd->mappedValue && !(allowMerge && Nd->isMerged)) { + assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); + assert(EA->isPointer(Nd->mappedValue)); + // Nodes must always be mapped from the pointer root value. + assert(Nd->mappedValue == EA->getPointerRoot(Nd->mappedValue)); } } #endif @@ -1488,9 +1579,6 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { #ifndef NDEBUG for (CGNode *Nd : Nodes) { - if (Nd->mappedValue && !(allowMerge && Nd->mergeTo)) - assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); - if (Nd->isMerged) { assert(Nd->mergeTo); assert(!Nd->pointsTo); @@ -1517,6 +1605,8 @@ void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { assert(PT->Type == NodeType::Content); assert(PT->findPred(Predecessor(Nd, EdgeType::PointsTo)) != PT->Preds.end()); } + if (Nd->isInterior()) + assert(Nd->pointsTo && "Interior content node requires a pointsTo node"); } #endif } @@ -1548,45 +1638,6 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } -EscapeAnalysis::CGNode * -EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { - CGNode *addrNode = conGraph->getNode(addrVal); - if (!addrNode) - return nullptr; - - if (CGNode *content = addrNode->getPointsToEdge()) - return content; - -#ifndef NDEBUG - if (!addrNode->isContent()) { - if (SILValue addrNodeValue = addrNode->getRepValue().getValue()) { - assert(isPointer(addrNodeValue)); - assert(addrNodeValue == getPointerRoot(addrVal)); - } - } -#endif - SILValue baseAddr = getPointerRoot(addrVal); - auto *F = addrVal->getFunction(); - auto hasRC = [&](){ - return mustContainReference(baseAddr->getType(), *F) - || mustContainReference(addrVal->getType(), *F); - }; - // Have we already merged a content node for this address? - if (CGNode *content = addrNode->getContentNodeOrNull()) { - // TODO: Optimistically merge hasRC content. The original content might not - // have an RC if one of the values pointing to this content was cast to an - // unknown type. If any of the types must contain a reference, then the - // content should contain a reference. - // - // For now, conservatively merge the RC flag instead. - if (content->hasRefCount() && !hasRC()) - content->setRefCount(false); - - return content; - } - return conGraph->createContentNode(addrNode, hasRC()); -} - void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1632,7 +1683,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (!BBArg->getSingleTerminatorOperands(Incoming)) { // We don't know where the block argument comes from -> treat it // conservatively. - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); continue; } CGNode *ArgNode = ConGraph->getNode(BBArg); @@ -1644,7 +1695,7 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, if (SrcArg) { ArgNode = ConGraph->defer(ArgNode, SrcArg); } else { - setEscapesGlobal(ConGraph, BBArg); + ConGraph->setEscapesGlobal(BBArg); break; } } @@ -1654,15 +1705,6 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns true if all uses of \p I are tuple_extract instructions. -static bool onlyUsedInTupleExtract(SILValue V) { - for (Operand *Use : getNonDebugUses(V)) { - if (!isa(Use->getUser())) - return false; - } - return true; -} - bool EscapeAnalysis::buildConnectionGraphForCallees( SILInstruction *Caller, CalleeList Callees, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1719,6 +1761,81 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } +EscapeAnalysis::ArrayUninitCall +EscapeAnalysis::canOptimizeArrayUninitializedCall(ApplyInst *ai, + ConnectionGraph *conGraph) { + ArrayUninitCall call; + // This must be an exact match so we don't accidentally optimize + // "array.uninitialized_intrinsic". + if (!ArraySemanticsCall(ai, "array.uninitialized", false)) + return call; + + // Check if the result is used in the usual way: extracting the + // array and the element pointer with tuple_extract. + for (Operand *use : getNonDebugUses(ai)) { + if (auto *tei = dyn_cast(use->getUser())) { + if (tei->getFieldNo() == 0) { + call.arrayStruct = tei; + continue; + } + if (tei->getFieldNo() == 1) { + call.arrayElementPtr = tei; + continue; + } + } + // If there are any other uses, such as a release_value, erase the previous + // call info and bail out. + call.arrayStruct = nullptr; + call.arrayElementPtr = nullptr; + break; + } + // An "array.uninitialized" call may have a first argument which is the + // allocated array buffer. Make sure the call's argument is recognized by + // EscapeAnalysis as a pointer, otherwise createArrayUninitializedSubgraph + // won't be able to map the result nodes onto it. There is a variant of + // @_semantics("array.uninitialized") that does not take the storage as input, + // so it will effectively bail out here. + if (isPointer(ai->getArgument(0))) + call.arrayStorageRef = ai->getArgument(0); + return call; +} + +bool EscapeAnalysis::canOptimizeArrayUninitializedResult( + TupleExtractInst *tei) { + ApplyInst *ai = dyn_cast(tei->getOperand()); + if (!ai) + return false; + + auto *conGraph = getConnectionGraph(ai->getFunction()); + return canOptimizeArrayUninitializedCall(ai, conGraph).isValid(); +} + +// Handle @_semantics("array.uninitialized") +// +// This call is analagous to a 'struct(storageRef)' instruction--we want a defer +// edge from the returned Array struct to the storage Reference that it +// contains. +// +// The returned unsafe pointer is handled simply by mapping the pointer value +// onto the object node that the storage argument points to. +void EscapeAnalysis::createArrayUninitializedSubgraph( + ArrayUninitCall call, ConnectionGraph *conGraph) { + CGNode *arrayStructNode = conGraph->getNode(call.arrayStruct); + assert(arrayStructNode && "Array struct must have a node"); + + CGNode *arrayRefNode = conGraph->getNode(call.arrayStorageRef); + assert(arrayRefNode && "canOptimizeArrayUninitializedCall checks isPointer"); + // If the arrayRefNode != null then arrayObjNode must be valid. + CGNode *arrayObjNode = conGraph->getValueContent(call.arrayStorageRef); + + // The reference argument is effectively stored inside the returned + // array struct. This is like struct(arrayRefNode). + conGraph->defer(arrayStructNode, arrayRefNode); + + // Map the returned element pointer to the array object's field pointer. + conGraph->setNode(call.arrayElementPtr, arrayObjNode); +} + void EscapeAnalysis::analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, @@ -1738,75 +1855,77 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case ArrayCallKind::kMakeMutable: // These array semantics calls do not capture anything. return; - case ArrayCallKind::kArrayUninitialized: - // Check if the result is used in the usual way: extracting the - // array and the element pointer with tuple_extract. - if (onlyUsedInTupleExtract(ASC.getCallResult())) { - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0))) { - SILValue ArrayBase = ASC.getCallResult(); - CGNode *ArrayContent = getValueContent(ConGraph, ArrayBase); - assert(ArrayContent && "Array base must have a node"); - ConGraph->defer(ArrayContent, BufferNode); - } + case ArrayCallKind::kArrayUninitialized: { + ArrayUninitCall call = canOptimizeArrayUninitializedCall( + cast(FAS.getInstruction()), ConGraph); + if (call.isValid()) { + createArrayUninitializedSubgraph(call, ConGraph); return; } break; + } case ArrayCallKind::kGetElement: - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { // The content of the destination address. - LoadedElement = getValueContent(ConGraph, FAS.getArgument(0)); + LoadedElement = ConGraph->getValueContent(FAS.getArgument(0)); assert(LoadedElement && "indirect result must have node"); } if (LoadedElement) { - CGNode *ArrayElementStorage = - ConGraph->getFieldContent(ArrayRefNode); - ConGraph->defer(LoadedElement, ArrayElementStorage); - return; + if (CGNode *arrayElementStorage = + ConGraph->getFieldContent(ArrayObjNode)) { + ConGraph->defer(LoadedElement, arrayElementStorage); + return; + } } } break; case ArrayCallKind::kGetElementAddress: - // This is like a ref_element_addr. - if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { - ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), ArrayRefNode); + // This is like a ref_element_addr. Both the object node and the + // returned address point to the same element storage. + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + CGNode *arrayElementAddress = ConGraph->getNode(ASC.getCallResult()); + ConGraph->defer(arrayElementAddress, ArrayObjNode); + return; } - return; + break; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *ArrayStructValue = - getValueContent(ConGraph, ASC.getSelf())) { + if (CGNode *ArrayStructNode = + ConGraph->getValueContent(ASC.getSelf())) { + // The first non indirect result is the closure. + auto Args = FAS.getArgumentsWithoutIndirectResults(); + ConGraph->setEscapesGlobal(Args[0]); // One content node for going from the array buffer pointer to // the element address (like ref_element_addr). - CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); - // TODO: If ArrayRefNode already exists, optimistically do - // ArrayRefNode->setRefCount(true). - if (!ArrayRefNode) { - ArrayRefNode = ConGraph->createContentNode( - ArrayStructValue, /*hasRC=*/true); + CGNode *ArrayObjNode = + ConGraph->getOrCreateContentNode(ArrayStructNode, + /*isInterior*/ true, + /*hasRefOnly*/ false); + // If ArrayObjNode was already potentially merged with its pointsTo, + // then conservatively mark the whole thing as escaping. + if (!ArrayObjNode->isInterior()) { + ArrayObjNode->markEscaping(); + return; } - // Another content node for the element storage. - CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); + // Otherwise, create the content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getOrCreateContentNode( + ArrayObjNode, /*isInterior*/ false, + /*hasRefOnly*/ true); ArrayElementStorage->markEscaping(); - // The first non indirect result is the closure. - auto Args = FAS.getArgumentsWithoutIndirectResults(); - setEscapesGlobal(ConGraph, Args[0]); return; } break; default: break; - } + } if (FAS.getReferencedFunctionOrNull() && FAS.getReferencedFunctionOrNull()->hasSemanticsAttr( @@ -1819,7 +1938,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The first not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[0]); + ConGraph->setEscapesGlobal(Args[0]); return; } @@ -1834,7 +1953,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The second not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[1]); + ConGraph->setEscapesGlobal(Args[1]); return; } @@ -1916,86 +2035,117 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { // A release instruction may deallocate the pointer operand. This may - // capture anything pointed to by the released object, but not the pointer - // to the object itself (because it will be a dangling pointer after - // deallocation). + // capture anything pointed to by the released object, but not the object + // itself (because it will be a dangling pointer after deallocation). SILValue OpV = I->getOperand(0); - CGNode *rcContent = getValueContent(ConGraph, OpV); - if (!rcContent) + CGNode *objNode = ConGraph->getValueContent(OpV); + if (!objNode) return; - // rcContent->hasRefCount() may or may not be true depending on whether - // the type could be analyzed. Either way, treat it structurally like a - // refcounted object. - CGNode *fieldContent = ConGraph->getFieldContent(rcContent); + CGNode *fieldNode = ConGraph->getFieldContent(objNode); + if (!fieldNode) { + // In the unexpected case that the object has no field content, create + // escaping unknown content. + ConGraph->getOrCreateUnknownContent(objNode)->markEscaping(); + return; + } if (!deinitIsKnownToNotCapture(OpV)) { - fieldContent->markEscaping(); + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + return; + } + // This deinit is known to not directly capture it's own field content; + // however, other secondary deinitializers could still capture anything + // pointed to by references within those fields. Since secondary + // deinitializers only apply to reference-type fields, not pointer-type + // fields, the "field" content can initially be considered an indirect + // reference. Unfortunately, we can't know all possible reference types + // that may eventually be associated with 'fieldContent', so we must + // assume here that 'fieldContent2' could hold raw pointers. This is + // implied by passing in invalid SILValue. + CGNode *objNode2 = + ConGraph->getOrCreateReferenceContent(SILValue(), fieldNode); + CGNode *fieldNode2 = objNode2->getContentNodeOrNull(); + ConGraph->getOrCreateUnknownContent(fieldNode2)->markEscaping(); + return; + } + case SILInstructionKind::DestroyAddrInst: { + SILValue addressVal = I->getOperand(0); + CGNode *valueNode = ConGraph->getValueContent(addressVal); + if (!valueNode) + return; + + // The value's destructor may escape anything the value points to. + // This could be an object referenced by the value or the contents of an + // existential box. + if (CGNode *fieldNode = ConGraph->getFieldContent(valueNode)) { + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); return; } - // This deinit is known to not directly capture it's own field content, - // however, indirect deinitializers could still capture anything pointed - // to by those fields. - ConGraph->escapeContentsOf(fieldContent); + ConGraph->getOrCreateUnknownContent(valueNode)->markEscaping(); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadInst: { assert(!cast(I)->getType().isAddress()); - LLVM_FALLTHROUGH; - case SILInstructionKind::RefElementAddrInst: - case SILInstructionKind::RefTailAddrInst: - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::OpenExistentialAddrInst: { - // Loads and projections into RC objects have a similar pattern: - // - // For RC object projections, get the non-address reference operand and - // return an RC content node that the reference directly points to. It is - // as-if the RC content node holds the pointer to the object fields. - // // For loads, get the address-type operand and return the content node // that the address directly points to. The load's address may itself come // from a ref_element_addr, project_box or open_existential, in which - // case, the loaded content will be the field content, not the RC content. + // case, the loaded content will be the field content, not the RC + // content. auto SVI = cast(I); if (!isPointer(SVI)) return; - SILValue pointerVal = SVI->getOperand(0); - if (CGNode *PointsTo = getValueContent(ConGraph, pointerVal)) { + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { + ConGraph->setNode(SVI, PointsTo); + return; + } + // A load from an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(SVI); + break; + } + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::OpenExistentialAddrInst: { + // For projections into objects, get the non-address reference operand and + // return an interior content node that the reference points to. + auto SVI = cast(I); + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { ConGraph->setNode(SVI, PointsTo); return; } // A load or projection from an address we don't handle -> be // conservative. - setEscapesGlobal(ConGraph, SVI); + ConGraph->setEscapesGlobal(SVI); return; } case SILInstructionKind::CopyAddrInst: { // Be conservative if the dest may be the final release. if (!cast(I)->isInitializationOfDest()) { setAllEscaping(I, ConGraph); - break; + return; } // A copy_addr is like a 'store (load src) to dest'. SILValue srcAddr = I->getOperand(CopyAddrInst::Src); - CGNode *loadedContent = getValueContent(ConGraph, srcAddr); + CGNode *loadedContent = ConGraph->getValueContent(srcAddr); if (!loadedContent) { setAllEscaping(I, ConGraph); break; } SILValue destAddr = I->getOperand(CopyAddrInst::Dest); // Create a defer-edge from the store location to the loaded content. - if (CGNode *destContent = getValueContent(ConGraph, destAddr)) { + if (CGNode *destContent = ConGraph->getValueContent(destAddr)) { ConGraph->defer(destContent, loadedContent); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcAddr); + ConGraph->setEscapesGlobal(srcAddr); return; } @@ -2016,13 +2166,13 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // (via ref_element_addr, project_box, or open_existential_addr) where the // stored field content is chained one level below the RC content. SILValue destAddr = I->getOperand(StoreInst::Dest); - if (CGNode *pointsTo = getValueContent(ConGraph, destAddr)) { + if (CGNode *pointsTo = ConGraph->getValueContent(destAddr)) { // Create a defer-edge from the content to the stored value. ConGraph->defer(pointsTo, valueNode); return; } // A store to an address we don't handle -> be conservative. - setEscapesGlobal(ConGraph, srcVal); + ConGraph->setEscapesGlobal(srcVal); return; } case SILInstructionKind::PartialApplyInst: { @@ -2062,15 +2212,10 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an // array.uninitialized call (otherwise getPointerBase should have already - // looked through it). The first result is the array itself. - // The second result (which is a pointer to the array elements) must be - // the content node of the first result. It's just like a ref_element_addr - // instruction. + // looked through it). auto *TEI = cast(I); - assert(isExtractOfArrayUninitializedPointer(TEI) + assert(canOptimizeArrayUninitializedResult(TEI) && "tuple_extract should be handled as projection"); - if (CGNode *ArrayElements = getValueContent(ConGraph, TEI->getOperand())) - ConGraph->setNode(TEI, ArrayElements); return; } case SILInstructionKind::UncheckedRefCastAddrInst: { @@ -2081,12 +2226,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, ConGraph->defer(DestNode, SrcNode); return; } - case SILInstructionKind::ReturnInst: - if (CGNode *ValueNd = - ConGraph->getNode(cast(I)->getOperand())) { + case SILInstructionKind::ReturnInst: { + SILValue returnVal = cast(I)->getOperand(); + if (CGNode *ValueNd = ConGraph->getNode(returnVal)) { ConGraph->defer(ConGraph->getReturnNode(), ValueNd); + ConGraph->getValueContent(returnVal)->mergeEscapeState( + EscapeState::Return); } return; + } default: // We handle all other instructions conservatively. setAllEscaping(I, ConGraph); @@ -2147,8 +2295,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { void EscapeAnalysis::setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph) { if (auto *TAI = dyn_cast(I)) { - setEscapesGlobal(ConGraph, TAI->getNormalBB()->getArgument(0)); - setEscapesGlobal(ConGraph, TAI->getErrorBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getNormalBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getErrorBB()->getArgument(0)); } // Even if the instruction does not write memory we conservatively set all // operands to escaping, because they may "escape" to the result value in @@ -2157,12 +2305,12 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I, for (const Operand &Op : I->getAllOperands()) { SILValue OpVal = Op.get(); if (!isNonWritableMemoryAddress(OpVal)) - setEscapesGlobal(ConGraph, OpVal); + ConGraph->setEscapesGlobal(OpVal); } // Even if the instruction does not write memory it could e.g. return the // address of global memory. Therefore we have to define it as escaping. for (auto result : I->getResults()) - setEscapesGlobal(ConGraph, result); + ConGraph->setEscapesGlobal(result); } void EscapeAnalysis::recompute(FunctionInfo *Initial) { @@ -2335,44 +2483,50 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, return SummaryGraph->mergeFrom(Graph, Mapping); } -bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph) { +// Return true if any content within the logical object pointed to by \p value +// escapes. +// +// Get the value's content node and check the escaping flag on all nodes within +// that object. An interior CG node points to content within the same object. +bool EscapeAnalysis::canEscapeToUsePoint(SILValue value, + SILInstruction *usePoint, + ConnectionGraph *conGraph) { - assert((FullApplySite::isa(UsePoint) || isa(UsePoint)) && - "use points are only created for calls and refcount instructions"); + assert((FullApplySite::isa(usePoint) || isa(usePoint)) + && "use points are only created for calls and refcount instructions"); - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) + CGNode *node = conGraph->getValueContent(value); + if (!node) return true; - // First check if there are escape paths which we don't explicitly see - // in the graph. - if (Node->valueEscapesInsideFunction(V)) - return true; + // Follow points-to edges and return true if the current 'node' may escape at + // 'usePoint'. + CGNodeWorklist worklist(conGraph); + while (node) { + // Merging arbitrary nodes is supported, which may lead to cycles of + // interior nodes. End the search. + if (!worklist.tryPush(node)) + break; - // No hidden escapes: check if the Node is reachable from the UsePoint. - // Check if the object itself can escape to the called function. - if (ConGraph->isUsePoint(UsePoint, Node)) - return true; + // First check if 'node' may escape in a way not represented by the + // connection graph, assuming that it may represent part of the object + // pointed to by 'value'. If 'node' happens to represent another object + // indirectly reachabe from 'value', then it cannot actually escape to this + // usePoint, so passing the original value is still conservatively correct. + if (node->valueEscapesInsideFunction(value)) + return true; - assert(isPointer(V) && "should not have a node for a non-pointer"); - - // Check if the object "content" can escape to the called function. - // This will catch cases where V is a reference and a pointer to a stored - // property escapes. - // It's also important in case of a pointer assignment, e.g. - // V = V1 - // apply(V1) - // In this case the apply is only a use-point for V1 and V1's content node. - // As V1's content node is the same as V's content node, we also make the - // check for the content node. - CGNode *ContentNode = getValueContent(ConGraph, V); - if (ContentNode->valueEscapesInsideFunction(V)) - return true; + // No hidden escapes; check if 'usePoint' may access memory at 'node'. + if (conGraph->isUsePoint(usePoint, node)) + return true; - if (ConGraph->isUsePoint(UsePoint, ContentNode)) - return true; + if (!node->isInterior()) + break; + // Continue to check for escaping content whenever 'content' may point to + // the same object as 'node'. + node = node->getContentNodeOrNull(); + } return false; } @@ -2410,24 +2564,6 @@ static SILFunction *getCommonFunction(SILValue V1, SILValue V2) { return F; } -bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { - if (!isUniquelyIdentified(V)) - return true; - - SILFunction *F = getCommonFunction(V, To); - if (!F) - return true; - auto *ConGraph = getConnectionGraph(F); - - CGNode *Node = ConGraph->getNodeOrNull(V); - if (!Node) - return true; - CGNode *ToNode = ConGraph->getNodeOrNull(To); - if (!ToNode) - return true; - return ConGraph->mayReach(ToNode, Node); -} - bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { // At least one of the values must be a non-escaping local object. bool isUniq1 = isUniquelyIdentified(V1); @@ -2440,27 +2576,26 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node1 = ConGraph->getNodeOrNull(V1); - if (!Node1) + CGNode *Content1 = ConGraph->getValueContent(V1); + if (!Content1) return true; - CGNode *Node2 = ConGraph->getNodeOrNull(V2); - if (!Node2) + + CGNode *Content2 = ConGraph->getValueContent(V2); + if (!Content2) return true; // Finish the check for one value being a non-escaping local object. - if (isUniq1 && Node1->valueEscapesInsideFunction(V1)) + if (isUniq1 && Content1->valueEscapesInsideFunction(V1)) isUniq1 = false; - if (isUniq2 && Node2->valueEscapesInsideFunction(V2)) + if (isUniq2 && Content2->valueEscapesInsideFunction(V2)) isUniq2 = false; if (!isUniq1 && !isUniq2) return true; // Check if both nodes may point to the same content. - CGNode *Content1 = getValueContent(ConGraph, V1); - CGNode *Content2 = getValueContent(ConGraph, V2); - + // FIXME!!!: This will be rewritten to use node flags in the next commit. SILType T1 = V1->getType(); SILType T2 = V2->getType(); if (T1.isAddress() && T2.isAddress()) { @@ -2483,33 +2618,97 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; } -bool EscapeAnalysis::canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam) { - CalleeList Callees = BCA->getCalleeList(FAS); - if (!Callees.allCalleesVisible()) +// Return true if deinitialization of \p releasedReference may release memory +// directly pointed to by \p accessAddress. +// +// Note that \p accessedAddress could be a reference itself, an address of a +// local/argument that contains a reference, or even a pointer to the middle of +// an object (even if it is an exclusive argument). +// +// This is almost the same as asking "is the content node for accessedAddress +// reachable via releasedReference", with three subtle differences: +// +// (1) A locally referenced object can only be freed when deinitializing +// releasedReference if it is the same object. Indirect references will be kept +// alive by their distinct local references--ARC can't remove those without +// inserting a mark_dependence/end_dependence scope. +// +// (2) the content of exclusive arguments may be indirectly reachable via +// releasedReference, but the exclusive argument must have it's own reference +// count, so cannot be freed via the locally released reference. +// +// (3) Objects may contain raw pointers into themselves or into other +// objects. Any access to the raw pointer is not considered a use of the object +// because that access must be "guarded" by a fix_lifetime or +// mark_dependence/end_dependence that acts as a placeholder. +// +// There are two interesting cases in which a connection graph query can +// determine that the accessed memory cannot be released: +// +// Case #1: accessedAddress points to a uniquely identified object that does not +// escape within this function. +// +// Note: A "uniquely identified object" is either a locally allocated object, +// which is obviously not reachable outside this function, or an exclusive +// address argument, which *is* reachable outside this function, but must +// have its own reference count so cannot be released locally. +// +// Case #2: The released reference points to a local object and no connection +// graph path exists from the referenced object to a global-escaping or +// argument-escaping node without traversing a non-interior edge. +// +// In both cases, the connection graph is sufficient to determine if the +// accessed content may be released. To prove that the accessed memory is +// distinct from any released memory it is now sufficient to check that no +// connection graph path exists from the released object's node to the accessed +// content node without traversing a non-interior edge. +bool EscapeAnalysis::mayReleaseContent(SILValue releasedReference, + SILValue accessedAddress) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); + + SILFunction *f = getCommonFunction(releasedReference, accessedAddress); + if (!f) + return true; + + auto *conGraph = getConnectionGraph(f); + + CGNode *addrContentNode = conGraph->getValueContent(accessedAddress); + if (!addrContentNode) return true; - // Derive the connection graph of the apply from the known callees. - for (SILFunction *Callee : Callees) { - FunctionInfo *FInfo = getFunctionInfo(Callee); - if (!FInfo->isValid()) - recompute(FInfo); + // Case #1: Unique accessedAddress whose content does not escape. + bool isAccessUniq = + isUniquelyIdentified(accessedAddress) + && !addrContentNode->valueEscapesInsideFunction(accessedAddress); - CGNode *Node = - FInfo->SummaryGraph.getNodeOrNull(Callee->getArgument(ParamIdx)); - if (!Node) - return true; + // Case #2: releasedReference points to a local object. + if (!isAccessUniq && !pointsToLocalObject(releasedReference)) + return true; - if (checkContentOfIndirectParam) { - Node = Node->getContentNodeOrNull(); - if (!Node) - continue; - } + CGNode *releasedObjNode = conGraph->getValueContent(releasedReference); + // Make sure we have at least one value CGNode for releasedReference. + if (!releasedObjNode) + return true; + + // Check for reachability from releasedObjNode to addrContentNode. + // A pointsTo cycle is equivalent to a null pointsTo. + CGNodeWorklist worklist(conGraph); + for (CGNode *releasedNode = releasedObjNode; + releasedNode && worklist.tryPush(releasedNode); + releasedNode = releasedNode->getContentNodeOrNull()) { + // A path exists from released content to accessed content. + if (releasedNode == addrContentNode) + return true; - if (Node->escapes()) + // A path exists to an escaping node. + if (!isAccessUniq && releasedNode->escapesInsideFunction()) return true; + + if (!releasedNode->isInterior()) + break; } - return false; + return false; // no path to escaping memory that may be freed. } void EscapeAnalysis::invalidate() { diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 6b5b0e29ff8a0..108a2545e1f60 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -322,7 +322,6 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { SILModule &M = F->getModule(); auto &Ctx = M.getASTContext(); GenericSignature NewGenericSig; - GenericEnvironment *NewGenericEnv; /// If the original function is generic, then maintain the same. auto OrigGenericSig = FTy->getInvocationGenericSignature(); @@ -341,8 +340,6 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { std::move(Requirements)}, GenericSignature()); - NewGenericEnv = NewGenericSig->getGenericEnvironment(); - /// Create a lambda for GenericParams. auto getCanonicalType = [&](Type t) -> CanType { return t->getCanonicalType(NewGenericSig); diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 40a136d26c687..2bb212748025c 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -160,6 +160,8 @@ class ReachingBlockSet { return !(*this == RHS); } + ReachingBlockSet(const ReachingBlockSet &RHS) + : Bits(RHS.Bits), NumBitWords(RHS.NumBitWords) {} const ReachingBlockSet &operator=(const ReachingBlockSet &RHS) { assert(NumBitWords == RHS.NumBitWords && "mismatched sets"); for (size_t i = 0, e = NumBitWords; i != e; ++i) @@ -1197,7 +1199,7 @@ constructClonedFunction(SILOptFunctionBuilder &FuncBuilder, /// 2. We only see a mark_uninitialized when paired with an (alloc_box, /// project_box). e.x.: /// -/// (mark_uninitialized (project_box (alloc_box))) +/// (project_box (mark_uninitialized (alloc_box))) /// /// The asserts are to make sure that if the initial safety condition check /// is changed, this code is changed as well. @@ -1209,15 +1211,16 @@ static SILValue getOrCreateProjectBoxHelper(SILValue PartialOperand) { } // Otherwise, handle the alloc_box case. If we have a mark_uninitialized on - // the box, we create the project value through that. + // the box, we know that we will have a project_box of that value due to SIL + // verifier invariants. SingleValueInstruction *Box = cast(PartialOperand); - if (auto *Op = Box->getSingleUse()) { - if (auto *MUI = dyn_cast(Op->getUser())) { - Box = MUI; + if (auto *MUI = Box->getSingleUserOfType()) { + if (auto *PBI = MUI->getSingleUserOfType()) { + return PBI; } } - // Just return a project_box. + // Otherwise, create a new project_box. SILBuilderWithScope B(std::next(Box->getIterator())); return B.createProjectBox(Box->getLoc(), Box, 0); } diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index d79c3961731e5..82be150b7ba62 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -567,7 +567,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, // Find the store instruction auto *BB = GetterF->getEntryBlock(); SILValue Val; - SILInstruction *Store; + SILInstruction *Store = nullptr; for (auto II = BB->begin(), E = BB->end(); II != E;) { auto &I = *II++; if (isa(&I)) { @@ -586,6 +586,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, B.createReturn(RI->getLoc(), Val); eraseUsesOfInstruction(RI); recursivelyDeleteTriviallyDeadInstructions(RI, true); + assert(Store && "Did not find a store?!"); recursivelyDeleteTriviallyDeadInstructions(Store, true); return GetterF; } diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 3f9380540c1be..53650a6bf37c8 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -991,7 +991,7 @@ void LoopTreeOptimization::hoistLoadsAndStores(SILValue addr, SILLoop *loop, Ins } // In case the value is only stored but never loaded in the loop. - recursivelyDeleteTriviallyDeadInstructions(initialLoad); + eliminateDeadInstruction(initialLoad); } bool LoopTreeOptimization::hoistAllLoadsAndStores(SILLoop *loop) { diff --git a/lib/SILOptimizer/Mandatory/CMakeLists.txt b/lib/SILOptimizer/Mandatory/CMakeLists.txt index b441b241f5e38..891c1ba40775e 100644 --- a/lib/SILOptimizer/Mandatory/CMakeLists.txt +++ b/lib/SILOptimizer/Mandatory/CMakeLists.txt @@ -13,7 +13,6 @@ silopt_register_sources( DiagnoseUnreachable.cpp GuaranteedARCOpts.cpp IRGenPrepare.cpp - MarkUninitializedFixup.cpp MandatoryInlining.cpp PredictableMemOpt.cpp PMOMemoryUseCollector.cpp diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index d0c9c67e5c49b..f3b863f32015a 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -143,8 +143,8 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, if (ReverseInitialWorklist) { std::reverse(insertedPhis.begin(), insertedPhis.end()); } - SmallVector worklist(insertedPhis.begin(), - insertedPhis.end()); + SmallVector worklist(insertedPhis.begin(), + insertedPhis.end()); sortUnique(insertedPhis); SmallVector incomingValues; @@ -187,9 +187,9 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, continue; auto *termInst = cast(user); - for (auto succBlockArgList : termInst->getSuccessorBlockArguments()) { + for (auto succBlockArgList : termInst->getSuccessorBlockArgumentLists()) { llvm::copy_if(succBlockArgList, std::back_inserter(worklist), - [&](SILPhiArgument *succArg) -> bool { + [&](SILArgument *succArg) -> bool { auto it = lower_bound(insertedPhis, succArg); return it != insertedPhis.end() && *it == succArg; }); diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 5188411efe102..76dc72b8b7c98 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -28,34 +28,32 @@ using namespace ownership; // Utility //===----------------------------------------------------------------------===// -static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI, - DIElementUseInfo &UseInfo) { - // The MUI must be used on an alloc_box, alloc_stack, or global_addr. If we - // have an alloc_stack or a global_addr, there is nothing further to do. - if (isa(MUI->getOperand()) || - isa(MUI->getOperand()) || - isa(MUI->getOperand()) || +static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, + DIElementUseInfo &useInfo) { + auto *uninitMemory = memoryInfo.getUninitializedValue(); + + // The uninitMemory must be used on an alloc_box, alloc_stack, or global_addr. + // If we have an alloc_stack or a global_addr, there is nothing further to do. + if (isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new // independent stack location for the pointer to address. - isa(MUI->getOperand())) + isa(uninitMemory->getOperand(0))) { return; + } - // Otherwise, we assume that we have a project_box. This is a hard cast to - // ensure that we catch any new patterns emitted by SILGen and assert. - auto *PBI = cast(MUI->getOperand()); - auto *ABI = cast(PBI->getOperand()); - - // Treat destroys of the container as load+destroys of the original value. + // Otherwise, we assume that we have an alloc_box. Treat destroys of the + // alloc_box as load+destroys of the value stored in the box. // // TODO: This should really be tracked separately from other destroys so that // we distinguish the lifetime of the container from the value itself. - for (auto *Op : ABI->getUses()) { - SILInstruction *User = Op->getUser(); - if (isa(User) || isa(User)) { - UseInfo.trackDestroy(User); - } + assert(isa(uninitMemory)); + auto *mui = cast(uninitMemory->getOperand(0)); + for (auto *user : mui->getUsersOfType()) { + useInfo.trackDestroy(user); } } @@ -98,7 +96,11 @@ static std::pair computeMemorySILType(MarkUninitializedInst *MemoryInst) { // Compute the type of the memory object. auto *MUI = MemoryInst; - SILType MemorySILType = MUI->getType().getObjectType(); + SILValue Address = MUI; + if (auto *PBI = Address->getSingleUserOfType()) { + Address = PBI; + } + SILType MemorySILType = Address->getType().getObjectType(); // If this is a let variable we're initializing, remember this so we don't // allow reassignment. @@ -210,12 +212,12 @@ SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const { Module, MemorySILType, EltNo, isNonDelegatingInit()); } -/// computeTupleElementAddress - Given a tuple element number (in the flattened -/// sense) return a pointer to a leaf element of the specified number. -SILValue DIMemoryObjectInfo::emitElementAddress( +/// Given a tuple element number (in the flattened sense) return a pointer to a +/// leaf element of the specified number, so we can insert destroys for it. +SILValue DIMemoryObjectInfo::emitElementAddressForDestroy( unsigned EltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> &EndBorrowList) const { - SILValue Ptr = getAddress(); + SmallVectorImpl> &EndScopeList) const { + SILValue Ptr = getUninitializedValue(); bool IsSelf = isNonDelegatingInit(); auto &Module = MemoryInst->getModule(); @@ -258,7 +260,7 @@ SILValue DIMemoryObjectInfo::emitElementAddress( if (isa(NTD) && Ptr->getType().isAddress()) { SILValue Original = Ptr; SILValue Borrowed = Ptr = B.createLoadBorrow(Loc, Ptr); - EndBorrowList.emplace_back(Borrowed, Original); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } } auto expansionContext = TypeExpansionContext(B.getFunction()); @@ -275,12 +277,13 @@ SILValue DIMemoryObjectInfo::emitElementAddress( if (Ptr.getOwnershipKind() != ValueOwnershipKind::Guaranteed) { Original = Ptr; Borrowed = Ptr = B.createBeginBorrow(Loc, Ptr); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } Ptr = B.createRefElementAddr(Loc, Ptr, VD); - if (Original) { - assert(Borrowed); - EndBorrowList.emplace_back(Borrowed, Original); - } + Ptr = B.createBeginAccess( + Loc, Ptr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + EndScopeList.emplace_back(Ptr, EndScopeKind::Access); } PointeeType = FieldType; @@ -512,28 +515,17 @@ static SILValue scalarizeLoad(LoadInst *LI, namespace { +/// Gathers information about a specific address and its uses to determine +/// definite initialization. class ElementUseCollector { SILModule &Module; const DIMemoryObjectInfo &TheMemory; DIElementUseInfo &UseInfo; - /// This is true if definite initialization has finished processing assign - /// and other ambiguous instructions into init vs assign classes. - bool isDefiniteInitFinished; - /// IsSelfOfNonDelegatingInitializer - This is true if we're looking at the /// top level of a 'self' variable in a non-delegating init method. bool IsSelfOfNonDelegatingInitializer; - /// How should address_to_pointer be handled? - /// - /// In DefiniteInitialization it is considered as an inout parameter to get - /// diagnostics about passing a let variable to an inout mutable-pointer - /// argument. - /// In PredictableMemOpt it is considered as an escape point to be - /// conservative. - bool TreatAddressToPointerAsInout; - /// When walking the use list, if we index into a struct element, keep track /// of this, so that any indexes into tuple subelements don't affect the /// element we attribute an access to. @@ -545,11 +537,8 @@ class ElementUseCollector { public: ElementUseCollector(const DIMemoryObjectInfo &TheMemory, - DIElementUseInfo &UseInfo, bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout) - : Module(TheMemory.MemoryInst->getModule()), TheMemory(TheMemory), - UseInfo(UseInfo), isDefiniteInitFinished(isDefiniteInitFinished), - TreatAddressToPointerAsInout(TreatAddressToPointerAsInout) {} + DIElementUseInfo &UseInfo) + : Module(TheMemory.getModule()), TheMemory(TheMemory), UseInfo(UseInfo) {} /// This is the main entry point for the use walker. It collects uses from /// the address and the refcount result of the allocation. @@ -558,7 +547,7 @@ class ElementUseCollector { // If this is a delegating initializer, collect uses specially. if (IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr) { + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr) { assert(!TheMemory.isDerivedClassSelfOnly() && "Should have been handled outside of here"); // If this is a class pointer, we need to look through ref_element_addrs. @@ -566,17 +555,16 @@ class ElementUseCollector { return; } - collectUses(TheMemory.MemoryInst, 0); - gatherDestroysOfContainer(TheMemory.MemoryInst, UseInfo); + collectUses(TheMemory.getUninitializedValue(), 0); + gatherDestroysOfContainer(TheMemory, UseInfo); } void trackUse(DIMemoryUse Use) { UseInfo.trackUse(Use); } void trackDestroy(SILInstruction *Destroy) { UseInfo.trackDestroy(Destroy); } - unsigned getNumMemoryElements() const { return TheMemory.NumElements; } - - SILInstruction *getMemoryInst() const { return TheMemory.MemoryInst; } + /// Return the raw number of elements including the 'super.init' value. + unsigned getNumMemoryElements() const { return TheMemory.getNumElements(); } private: void collectUses(SILValue Pointer, unsigned BaseEltNo); @@ -587,8 +575,6 @@ class ElementUseCollector { void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, DIUseKind Kind); void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo); - void collectDestructureTupleResultUses(DestructureTupleResult *DTR, - unsigned BaseEltNo); void collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo); }; @@ -603,7 +589,8 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, // If we're in a subelement of a struct or enum, just mark the struct, not // things that come after it in a parent tuple. unsigned NumElements = 1; - if (TheMemory.NumElements != 1 && !InStructSubElement && !InEnumSubElement) + if (TheMemory.getNumElements() != 1 && !InStructSubElement && + !InEnumSubElement) NumElements = getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, UseTy, IsSelfOfNonDelegatingInitializer); @@ -640,34 +627,6 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, collectUses(TEAI, BaseEltNo); } -/// Given a destructure_tuple, compute the new BaseEltNo implicit in the -/// selected member, and recursively add uses of the instruction. -void ElementUseCollector::collectDestructureTupleResultUses( - DestructureTupleResult *DTR, unsigned BaseEltNo) { - - // If we're walking into a tuple within a struct or enum, don't adjust the - // BaseElt. The uses hanging off the tuple_element_addr are going to be - // counted as uses of the struct or enum itself. - if (InStructSubElement || InEnumSubElement) - return collectUses(DTR, BaseEltNo); - - assert(!IsSelfOfNonDelegatingInitializer && "self doesn't have tuple type"); - - // tuple_element_addr P, 42 indexes into the current tuple element. - // Recursively process its uses with the adjusted element number. - unsigned FieldNo = DTR->getIndex(); - auto T = DTR->getParent()->getOperand()->getType(); - if (T.is()) { - for (unsigned i = 0; i != FieldNo; ++i) { - SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(TypeExpansionContext(*DTR->getFunction()), - Module, EltTy, false); - } - } - - collectUses(DTR, BaseEltNo); -} - void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo) { // Generally, we set the "InStructSubElement" flag and recursively process @@ -733,15 +692,15 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } - // Look through begin_access and begin_borrow - if (isa(User) || isa(User)) { + // Look through begin_access. + if (isa(User)) { auto begin = cast(User); collectUses(begin, BaseEltNo); continue; } - // Ignore end_access and end_borrow. - if (isa(User) || isa(User)) { + // Ignore end_access. + if (isa(User)) { continue; } @@ -803,8 +762,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; \ else if (SWI->isInitializationOfDest()) \ Kind = DIUseKind::Initialization; \ - else if (isDefiniteInitFinished) \ - Kind = DIUseKind::Assign; \ else \ Kind = DIUseKind::InitOrAssign; \ trackUse(DIMemoryUse(User, Kind, BaseEltNo, 1)); \ @@ -831,8 +788,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; else if (CAI->isInitializationOfDest()) Kind = DIUseKind::Initialization; - else if (isDefiniteInitFinished) - Kind = DIUseKind::Assign; else Kind = DIUseKind::InitOrAssign; @@ -890,14 +845,14 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // by the callee, and this is enforced by sema, so we can consider it // a nonmutating use. bool isLet = true; - - for (unsigned i = 0; i < TheMemory.NumElements; ++i) { + + for (unsigned i = 0; i < TheMemory.getNumElements(); ++i) { if (!TheMemory.isElementLetProperty(i)) { isLet = false; break; } } - + if (isLet) { addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn); continue; @@ -912,7 +867,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // inout use. DIUseKind Kind; if (TheMemory.isStructInitSelf() && - getAccessedPointer(Pointer) == TheMemory.getAddress()) { + getAccessedPointer(Pointer) == TheMemory.getUninitializedValue()) { Kind = DIUseKind::Escape; } else if (Apply.hasSelfArgument() && Op == &Apply.getSelfArgumentOperand()) { @@ -928,7 +883,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { llvm_unreachable("bad parameter convention"); } - if (isa(User) && TreatAddressToPointerAsInout) { + if (isa(User)) { // address_to_pointer is a mutable escape, which we model as an inout use. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutArgument); @@ -1114,9 +1069,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { collectTupleElementUses(TEAI, BaseEltNo); continue; } - - auto *DTRI = cast(EltPtr); - collectDestructureTupleResultUses(DTRI, BaseEltNo); } } } @@ -1125,20 +1077,19 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { /// constructor. The memory object has class type. void ElementUseCollector::collectClassSelfUses() { assert(IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr); + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr); // For efficiency of lookup below, compute a mapping of the local ivars in the // class to their element number. llvm::SmallDenseMap EltNumbering; { - SILType T = TheMemory.MemorySILType; + SILType T = TheMemory.getType(); auto *NTD = T.getNominalOrBoundGenericNominal(); unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) { EltNumbering[VD] = NumElements; - auto expansionContext = - TypeExpansionContext(*TheMemory.MemoryInst->getFunction()); + auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); NumElements += getElementCountRec( expansionContext, Module, T.getFieldType(VD, Module, expansionContext), false); @@ -1147,9 +1098,9 @@ void ElementUseCollector::collectClassSelfUses() { // If we are looking at the init method for a root class, just walk the // MUI use-def chain directly to find our uses. - auto *MemoryInst = TheMemory.MemoryInst; - if (MemoryInst->getKind() == MarkUninitializedInst::RootSelf) { - collectClassSelfUses(MemoryInst, TheMemory.MemorySILType, EltNumbering); + if (TheMemory.isRootSelf()) { + collectClassSelfUses(TheMemory.getUninitializedValue(), TheMemory.getType(), + EltNumbering); return; } @@ -1166,7 +1117,7 @@ void ElementUseCollector::collectClassSelfUses() { // 4) Potential escapes after super.init, if self is closed over. // // Handle each of these in turn. - SmallVector Uses(MemoryInst->getUses()); + SmallVector Uses(TheMemory.getUninitializedValue()->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1177,7 +1128,7 @@ void ElementUseCollector::collectClassSelfUses() { // The initial store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MemoryInst->getParent()) { + if (Arg->getParent() == TheMemory.getParentBlock()) { StoresOfArgumentToSelf++; continue; } @@ -1192,7 +1143,7 @@ void ElementUseCollector::collectClassSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MemoryInst) + if (LI->getOperand() == TheMemory.getUninitializedValue()) continue; // Any other store needs to be recorded. @@ -1217,7 +1168,7 @@ void ElementUseCollector::collectClassSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { auto load = cast(User); - collectClassSelfUses(load, TheMemory.MemorySILType, EltNumbering); + collectClassSelfUses(load, TheMemory.getType(), EltNumbering); continue; } @@ -1236,7 +1187,7 @@ void ElementUseCollector::collectClassSelfUses() { // We can safely handle anything else as an escape. They should all happen // after super.init is invoked. As such, all elements must be initialized // and super.init must be called. - trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.getNumElements())); } assert(StoresOfArgumentToSelf == 1 && @@ -1511,7 +1462,7 @@ void ElementUseCollector::collectClassSelfUses( Kind = DIUseKind::Escape; } - trackUse(DIMemoryUse(User, Kind, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, Kind, 0, TheMemory.getNumElements())); } } @@ -1631,7 +1582,7 @@ class ClassInitElementUseCollector { // *NOTE* Even though this takes a SILInstruction it actually only accepts // load_borrow and load instructions. This is enforced via an assert. - void collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, + void collectClassInitSelfLoadUses(SingleValueInstruction *MUI, SingleValueInstruction *LI); }; @@ -1643,22 +1594,22 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // When we're analyzing a delegating constructor, we aren't field sensitive at // all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit"); - auto *MUI = TheMemory.MemoryInst; + assert(TheMemory.getNumElements() == 1 && "delegating inits only have 1 bit"); + auto *uninitMemory = TheMemory.getUninitializedValue(); // The number of stores of the initial 'self' argument into the self box // that we saw. unsigned StoresOfArgumentToSelf = 0; - // We walk the use chains of the self MUI to find any accesses to it. The - // possible uses are: + // We walk the use chains of the self uninitMemory to find any accesses to it. + // The possible uses are: // 1) The initialization store. // 2) Loads of the box, which have uses of self hanging off of them. // 3) An assign to the box, which happens at super.init. // 4) Potential escapes after super.init, if self is closed over. // Handle each of these in turn. // - SmallVector Uses(MUI->getUses()); + SmallVector Uses(uninitMemory->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1682,7 +1633,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // A store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MUI->getParent()) { + if (Arg->getParent() == uninitMemory->getParent()) { StoresOfArgumentToSelf++; continue; } @@ -1697,7 +1648,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MUI) + if (LI->getOperand() == uninitMemory) continue; // Any other store needs to be recorded. @@ -1747,7 +1698,8 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { - collectClassInitSelfLoadUses(MUI, cast(User)); + collectClassInitSelfLoadUses(uninitMemory, + cast(User)); continue; } @@ -1764,14 +1716,11 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { assert(StoresOfArgumentToSelf == 1 && "The 'self' argument should have been stored into the box exactly once"); - - // Gather the uses of the - gatherDestroysOfContainer(MUI, UseInfo); } -void ClassInitElementUseCollector:: -collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, - SingleValueInstruction *LI) { +void ClassInitElementUseCollector::collectClassInitSelfLoadUses( + SingleValueInstruction *MUI, SingleValueInstruction *LI) { + assert(isa(MUI) || isa(MUI)); assert(isa(LI) || isa(LI)); // If we have a load, then this is a use of the box. Look at the uses of @@ -1853,24 +1802,24 @@ collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, //===----------------------------------------------------------------------===// static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { - if (MemoryInfo.MemoryInst->isDelegatingSelfAllocated()) + if (MemoryInfo.isDelegatingSelfAllocated()) return true; return MemoryInfo.isNonDelegatingInit() && - MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr && + MemoryInfo.getASTType()->getClassOrBoundGenericClass() != nullptr && MemoryInfo.isDerivedClassSelfOnly(); } -/// collectDIElementUsesFrom - Analyze all uses of the specified allocation -/// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them -/// and storing the information found into the Uses and Releases lists. +/// Analyze all uses of the specified allocation instruction (alloc_box, +/// alloc_stack or mark_uninitialized), classifying them and storing the +/// information found into the Uses and Releases lists. void swift::ownership::collectDIElementUsesFrom( - const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo, - bool isDIFinished, bool TreatAddressToPointerAsInout) { + const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo) { if (shouldPerformClassInitSelf(MemoryInfo)) { ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo); UseCollector.collectClassInitSelfUses(); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } @@ -1878,12 +1827,13 @@ void swift::ownership::collectDIElementUsesFrom( // When we're analyzing a delegating constructor, we aren't field sensitive // at all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(MemoryInfo.NumElements == 1 && "delegating inits only have 1 bit"); - collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.MemoryInst); + assert(MemoryInfo.getNumElements() == 1 && + "delegating inits only have 1 bit"); + collectDelegatingInitUses(MemoryInfo, UseInfo, + MemoryInfo.getUninitializedValue()); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } - ElementUseCollector(MemoryInfo, UseInfo, isDIFinished, - TreatAddressToPointerAsInout) - .collectFrom(); + ElementUseCollector(MemoryInfo, UseInfo).collectFrom(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index cc9b8c56add95..66a878cc5717f 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -52,7 +52,6 @@ struct DIElementUseInfo; /// Derived classes have an additional field at the end that models whether or /// not super.init() has been called or not. class DIMemoryObjectInfo { -public: /// The uninitialized memory that we are analyzing. MarkUninitializedInst *MemoryInst; @@ -61,7 +60,8 @@ class DIMemoryObjectInfo { /// This is the count of elements being analyzed. For memory objects that are /// tuples, this is the flattened element count. For 'self' members in init - /// methods, this is the local field count (+1 for derive classes). + /// methods, this is the local field count (+1 for super/self classes were + /// initialized). unsigned NumElements; /// True if the memory object being analyzed represents a 'let', which is @@ -77,27 +77,55 @@ class DIMemoryObjectInfo { SILLocation getLoc() const { return MemoryInst->getLoc(); } SILFunction &getFunction() const { return *MemoryInst->getFunction(); } + SILModule &getModule() const { return MemoryInst->getModule(); } + SILBasicBlock *getParentBlock() const { return MemoryInst->getParent(); } /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; - CanType getType() const { return MemorySILType.getASTType(); } + CanType getASTType() const { return MemorySILType.getASTType(); } + SILType getType() const { return MemorySILType; } + + /// Returns true if this memory object is of trivial type. + bool hasTrivialType() const { return MemorySILType.isTrivial(getFunction()); } + + /// Returns true if NumElements has a dummy value in it to force a struct to + /// be non-empty. + bool hasDummyElement() const { return HasDummyElement; } - SingleValueInstruction *getAddress() const { return MemoryInst; } + /// Return the actual 'uninitialized' memory. In the case of alloc_ref, + /// alloc_stack, this always just returns the actual mark_uninitialized + /// instruction. For alloc_box though it returns the project_box associated + /// with the memory info. + SingleValueInstruction *getUninitializedValue() const { + if (auto *mui = dyn_cast(MemoryInst)) { + if (auto *pbi = mui->getSingleUserOfType()) { + return pbi; + } + } + return MemoryInst; + } - /// getNumMemoryElements - Return the number of elements, without the extra - /// "super.init" tracker in initializers of derived classes. + /// Return the number of elements, without the extra "super.init" tracker in + /// initializers of derived classes. unsigned getNumMemoryElements() const { return NumElements - (unsigned)isDerivedClassSelf(); } - /// isAnyInitSelf - Return true if this is 'self' in any kind of initializer. + /// Return the number of elements, including the extra "super.init" tracker in + /// initializers of derived classes. + /// + /// \see getNumMemoryElements() for the number of elements, excluding the + /// extra "super.init" tracker in the initializers of derived classes. + unsigned getNumElements() const { return NumElements; } + + /// Return true if this is 'self' in any kind of initializer. bool isAnyInitSelf() const { return !MemoryInst->isVar(); } /// True if the memory object is the 'self' argument of a struct initializer. bool isStructInitSelf() const { if (MemoryInst->isRootSelf() || MemoryInst->isCrossModuleRootSelf()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -123,7 +151,7 @@ class DIMemoryObjectInfo { return false; if (!MemoryInst->isVar()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -176,14 +204,23 @@ class DIMemoryObjectInfo { return false; } - /// emitElementAddress - Given an element number (in the flattened sense) - /// return a pointer to a leaf element of the specified number. - SILValue - emitElementAddress(unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> - &EndBorrowList) const; + bool isRootSelf() const { + return MemoryInst->getKind() == MarkUninitializedInst::RootSelf; + } + + bool isDelegatingSelfAllocated() const { + return MemoryInst->isDelegatingSelfAllocated(); + } + + enum class EndScopeKind { Borrow, Access }; + + /// Given an element number (in the flattened sense) return a pointer to a + /// leaf element of the specified number. + SILValue emitElementAddressForDestroy( + unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, + SmallVectorImpl> &EndScopeList) const; - /// getElementType - Return the swift type of the specified element. + /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; /// Push the symbolic path name to the specified element number onto the @@ -294,9 +331,7 @@ struct DIElementUseInfo { /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, - DIElementUseInfo &UseInfo, - bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout); + DIElementUseInfo &UseInfo); } // end namespace ownership } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 531d68083f6be..1574b966d0e12 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -435,8 +435,9 @@ namespace { void emitSelfConsumedDiagnostic(SILInstruction *Inst); LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) { - return PerBlockInfo.insert({BB, - LiveOutBlockState(TheMemory.NumElements)}).first->second; + return PerBlockInfo + .insert({BB, LiveOutBlockState(TheMemory.getNumElements())}) + .first->second; } AvailabilitySet getLivenessAtInst(SILInstruction *Inst, unsigned FirstElt, @@ -476,10 +477,14 @@ namespace { void processUninitializedRelease(SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); - void processUninitializedReleaseOfBox(AllocBoxInst *ABI, + + /// Process a mark_uninitialized of an alloc_box that is uninitialized and + /// needs a dealloc_box. + void processUninitializedReleaseOfBox(MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); + void deleteDeadRelease(unsigned ReleaseID); void processNonTrivialRelease(unsigned ReleaseID); @@ -508,12 +513,9 @@ namespace { LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, DIElementUseInfo &UseInfo) - : F(*TheMemory.MemoryInst->getFunction()), - Module(TheMemory.MemoryInst->getModule()), - TheMemory(TheMemory), - Uses(UseInfo.Uses), - StoresToSelf(UseInfo.StoresToSelf), - Destroys(UseInfo.Releases) { + : F(TheMemory.getFunction()), Module(TheMemory.getModule()), + TheMemory(TheMemory), Uses(UseInfo.Uses), + StoresToSelf(UseInfo.StoresToSelf), Destroys(UseInfo.Releases) { // The first step of processing an element is to collect information about the // element into data structures we use later. @@ -558,10 +560,10 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, getBlockInfo(bb).markStoreToSelf(); } - // If isn't really a use, but we account for the alloc_box/mark_uninitialized - // as a use so we see it in our dataflow walks. - NonLoadUses[TheMemory.MemoryInst] = ~0U; - auto &MemBBInfo = getBlockInfo(TheMemory.MemoryInst->getParent()); + // If isn't really a use, but we account for the mark_uninitialized or + // project_box as a use so we see it in our dataflow walks. + NonLoadUses[TheMemory.getUninitializedValue()] = ~0U; + auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock()); MemBBInfo.HasNonLoadUse = true; // There is no scanning required (or desired) for the block that defines the @@ -640,7 +642,7 @@ void LifetimeChecker::noteUninitializedMembers(const DIMemoryUse &Use) { if (Liveness.get(i) == DIKind::Yes) continue; // Ignore a failed super.init requirement. - if (i == TheMemory.NumElements-1 && TheMemory.isDerivedClassSelf()) + if (i == TheMemory.getNumElements() - 1 && TheMemory.isDerivedClassSelf()) continue; std::string Name; @@ -672,7 +674,7 @@ std::string LifetimeChecker::getUninitElementName(const DIMemoryUse &Use) { // Verify that it isn't the super.init marker that failed. The client should // handle this, not pass it down to diagnoseInitError. assert((!TheMemory.isDerivedClassSelf() || - firstUndefElement != TheMemory.NumElements-1) && + firstUndefElement != TheMemory.getNumElements() - 1) && "super.init failure not handled in the right place"); // If the definition is a declaration, try to reconstruct a name and @@ -792,11 +794,12 @@ void LifetimeChecker::doIt() { // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process // releases to adjust for this. - if (!TheMemory.MemorySILType.isTrivial(F)) { + if (!TheMemory.hasTrivialType()) { + // NOTE: This array may increase in size! for (unsigned i = 0, e = Destroys.size(); i != e; ++i) processNonTrivialRelease(i); } - + // If the memory object had any non-trivial stores that are init or assign // based on the control flow path reaching them, then insert dynamic control // logic and CFG diamonds to handle this. @@ -805,7 +808,7 @@ void LifetimeChecker::doIt() { HasConditionalDestroy || HasConditionalSelfInitialized) { ControlVariable = handleConditionalInitAssign(); - SILValue memAddr = TheMemory.MemoryInst->getOperand(); + SILValue memAddr = TheMemory.getUninitializedValue()->getOperand(0); if (auto *ASI = dyn_cast(memAddr)) { ASI->setDynamicLifetime(); } else if (auto *ABI = dyn_cast(memAddr)) { @@ -938,8 +941,8 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { emitSelfConsumedDiagnostic(Use.Inst); return; @@ -1006,7 +1009,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *projection = dyn_cast(addr)) addr = projection->getOperand(); - return addr == TheMemory.getAddress(); + return addr == TheMemory.getUninitializedValue(); }; if (!isFullyInitialized && WantsCrossModuleStructInitializerDiagnostic && @@ -1020,7 +1023,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *ctor = fnLoc.getAsASTNode()) selfTy = ctor->getImplicitSelfDecl()->getType(); else - selfTy = TheMemory.getType(); + selfTy = TheMemory.getASTType(); StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1284,7 +1287,7 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { diagnose(Module, Inst->getLoc(), diag::self_before_selfinit_value_type); if (!HasSuggestedNoArgSelfInit && FullyUninitialized) { auto *maybeStruct = - TheMemory.getType().getStructOrBoundGenericStruct(); + TheMemory.getASTType().getStructOrBoundGenericStruct(); maybeSuggestNoArgSelfInit(Module, Inst->getLoc(), maybeStruct); HasSuggestedNoArgSelfInit = true; } @@ -1400,7 +1403,7 @@ findMethodForStoreInitializationOfTemporary(const DIMemoryObjectInfo &TheMemory, // argument, so the ownership verifier would trip. So we know that such a // thing can not happen. On the other hand, for store_borrow, we need to // strip the borrow, so lets use idempotence for correctness. - if (stripBorrow(SI->getSrc()) != TheMemory.MemoryInst || + if (stripBorrow(SI->getSrc()) != TheMemory.getUninitializedValue() || !isa(SI->getDest()) || !TheMemory.isClassInitSelf()) { return nullptr; } @@ -1618,9 +1621,8 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties( if (!shouldEmitError(Inst)) return true; - if (TheMemory.isCrossModuleStructInitSelf() && - TheMemory.HasDummyElement) { - Type selfTy = TheMemory.getType(); + if (TheMemory.isCrossModuleStructInitSelf() && TheMemory.hasDummyElement()) { + Type selfTy = TheMemory.getASTType(); const StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1653,8 +1655,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use, // Stores back to the 'self' box are OK. if (auto store = dyn_cast(Inst)) { - if (store->getDest() == TheMemory.MemoryInst - && TheMemory.isClassInitSelf()) + if (store->getDest() == TheMemory.getUninitializedValue() && + TheMemory.isClassInitSelf()) return; } @@ -1813,15 +1815,16 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { assert(TheMemory.isAnyInitSelf()); assert(!TheMemory.isClassInitSelf() || TheMemory.isNonRootClassSelf()); - assert(TheMemory.getType()->hasReferenceSemantics()); + assert(TheMemory.getASTType()->hasReferenceSemantics()); // Determine the liveness states of the memory object, including the // self/super.init state. - AvailabilitySet Liveness = getLivenessAtInst(Inst, 0, TheMemory.NumElements); + AvailabilitySet Liveness = + getLivenessAtInst(Inst, 0, TheMemory.getNumElements()); // self/super.init() calls require that self/super.init has not already // been called. If it has, reject the program. - switch (Liveness.get(TheMemory.NumElements-1)) { + switch (Liveness.get(TheMemory.getNumElements() - 1)) { case DIKind::No: // This is good! Keep going. break; case DIKind::Yes: @@ -1839,7 +1842,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } if (TheMemory.isDelegatingInit()) { - assert(TheMemory.NumElements == 1 && "delegating inits have a single elt"); + assert(TheMemory.getNumElements() == 1 && + "delegating inits have a single elt"); // Lower Assign instructions if needed. if (isa(Use.Inst) || isa(Use.Inst)) @@ -1847,7 +1851,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } else { // super.init also requires that all ivars are initialized before the // superclass initializer runs. - for (unsigned i = 0, e = TheMemory.NumElements-1; i != e; ++i) { + for (unsigned i = 0, e = TheMemory.getNumElements() - 1; i != e; ++i) { if (Liveness.get(i) == DIKind::Yes) continue; // If the super.init call is implicit generated, produce a specific @@ -1942,12 +1946,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { } void LifetimeChecker::processUninitializedReleaseOfBox( - AllocBoxInst *ABI, SILInstruction *Release, bool consumed, + MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt) { - assert(ABI == Release->getOperand(0)); + assert(isa(MUI->getOperand())); + assert(MUI == Release->getOperand(0)); SILBuilderWithScope B(Release); B.setInsertionPoint(InsertPt); - Destroys.push_back(B.createDeallocBox(Release->getLoc(), ABI)); + Destroys.push_back(B.createDeallocBox(Release->getLoc(), MUI)); } void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, @@ -1957,8 +1962,10 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // dealloc_partial_ref to free the memory. If this is a derived class, we // may have to do a load of the 'self' box to get the class reference. if (!TheMemory.isClassInitSelf()) { - if (auto *ABI = dyn_cast(Release->getOperand(0))) { - return processUninitializedReleaseOfBox(ABI, Release, consumed, InsertPt); + if (auto *MUI = dyn_cast(Release->getOperand(0))) { + if (isa(MUI->getOperand())) { + return processUninitializedReleaseOfBox(MUI, Release, consumed, InsertPt); + } } return; } @@ -1973,15 +1980,20 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // If we see an alloc_box as the pointer, then we're deallocating a 'box' for // self. Make sure that the box gets deallocated (not released) since the // pointer it contains will be manually cleaned up. - auto *ABI = dyn_cast(Release->getOperand(0)); - if (ABI) - Pointer = getOrCreateProjectBox(ABI, 0); + auto *MUI = dyn_cast(Release->getOperand(0)); + + if (MUI && isa(MUI->getOperand())) { + Pointer = MUI->getSingleUserOfType(); + assert(Pointer); + } else { + MUI = nullptr; + } if (!consumed) { if (Pointer->getType().isAddress()) Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take); - auto MetatypeTy = CanMetatypeType::get(TheMemory.MemorySILType.getASTType(), + auto MetatypeTy = CanMetatypeType::get(TheMemory.getASTType(), MetatypeRepresentation::Thick); auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); SILValue Metatype; @@ -2000,8 +2012,8 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, } // dealloc_box the self box if necessary. - if (ABI) { - auto DB = B.createDeallocBox(Loc, ABI); + if (MUI) { + auto DB = B.createDeallocBox(Loc, MUI); Destroys.push_back(DB); } } @@ -2010,8 +2022,13 @@ void LifetimeChecker::deleteDeadRelease(unsigned ReleaseID) { SILInstruction *Release = Destroys[ReleaseID]; if (isa(Release)) { SILValue Addr = Release->getOperand(0); - if (auto *AddrI = Addr->getDefiningInstruction()) + if (auto *AddrI = Addr->getDefiningInstruction()) { + // FIXME: AddrI will not be deleted (nor its operands) when Release is + // still using AddrI's result. Fix this, and migrate to using + // InstructionDeleter utility instead of + // recursivelyDeadTriviallyDeadInstructions. recursivelyDeleteTriviallyDeadInstructions(AddrI); + } } Release->eraseFromParent(); Destroys[ReleaseID] = nullptr; @@ -2041,7 +2058,7 @@ void LifetimeChecker::processNonTrivialRelease(unsigned ReleaseID) { assert(isa(Release) || isa(Release) || isa(Release)); - auto Availability = getLivenessAtInst(Release, 0, TheMemory.NumElements); + auto Availability = getLivenessAtInst(Release, 0, TheMemory.getNumElements()); DIKind SelfInitialized = DIKind::Yes; if (TheMemory.isNonRootClassSelf()) { @@ -2184,7 +2201,7 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { SILLocation Loc = TheMemory.getLoc(); Loc.markAutoGenerated(); - unsigned NumMemoryElements = TheMemory.NumElements; + unsigned NumMemoryElements = TheMemory.getNumElements(); // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2213,7 +2230,8 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { } // Before the memory allocation, store zero in the control variable. - auto *InsertPoint = &*std::next(TheMemory.MemoryInst->getIterator()); + auto *InsertPoint = + &*std::next(TheMemory.getUninitializedValue()->getIterator()); B.setInsertionPoint(InsertPoint); B.setCurrentDebugScope(InsertPoint->getDebugScope()); SILValue ControlVariableAddr = ControlVariableBox; @@ -2285,14 +2303,26 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { B.setInsertionPoint(TrueBB->begin()); SILValue EltPtr; { - llvm::SmallVector, 4> EndBorrowList; - EltPtr = TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; + EltPtr = + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } } B.setInsertionPoint(ContBB->begin()); @@ -2331,13 +2361,13 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { /// to emit branching logic when an element may or may not be initialized. void LifetimeChecker:: handleConditionalDestroys(SILValue ControlVariableAddr) { - SILBuilderWithScope B(TheMemory.MemoryInst); + SILBuilderWithScope B(TheMemory.getUninitializedValue()); Identifier ShiftRightFn, TruncateFn; - unsigned NumMemoryElements = TheMemory.NumElements; - - unsigned SelfInitializedElt = TheMemory.NumElements; - unsigned SuperInitElt = TheMemory.NumElements - 1; + unsigned NumMemoryElements = TheMemory.getNumElements(); + + unsigned SelfInitializedElt = TheMemory.getNumElements(); + unsigned SuperInitElt = TheMemory.getNumElements() - 1; // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2346,15 +2376,27 @@ handleConditionalDestroys(SILValue ControlVariableAddr) { // Utilities. auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) { - llvm::SmallVector, 4> EndBorrowList; + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; SILValue EltPtr = - TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } }; @@ -2668,7 +2710,8 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst, // is not defined at all yet. Otherwise, we've found a definition, or // something else that will require that the memory is initialized at // this point. - Result.set(0, TheInst == TheMemory.MemoryInst ? DIKind::No : DIKind::Yes); + Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No + : DIKind::Yes); return Result; } } @@ -2694,7 +2737,7 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, LLVM_DEBUG(llvm::dbgs() << "Get liveness " << FirstElt << ", #" << NumElts << " at " << *Inst); - AvailabilitySet Result(TheMemory.NumElements); + AvailabilitySet Result(TheMemory.getNumElements()); // Empty tuple queries return a completely "unknown" vector, since they don't // care about any of the elements. @@ -2705,13 +2748,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // The vastly most common case is memory allocations that are not tuples, // so special case this with a more efficient algorithm. - if (TheMemory.NumElements == 1) { + if (TheMemory.getNumElements() == 1) { return getLivenessAtNonTupleInst(Inst, InstBB, Result); } // Check locally to see if any elements are satisfied within the block, and // keep track of which ones are still needed in the NeededElements set. - SmallBitVector NeededElements(TheMemory.NumElements); + SmallBitVector NeededElements(TheMemory.getNumElements()); NeededElements.set(FirstElt, FirstElt+NumElts); // If there is a store in the current block, scan the block to see if the @@ -2729,13 +2772,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // If we found the allocation itself, then we are loading something that // is not defined at all yet. Scan no further. - if (TheInst == TheMemory.MemoryInst) { + if (TheInst == TheMemory.getUninitializedValue()) { // The result is perfectly decided locally. for (unsigned i = FirstElt, e = i+NumElts; i != e; ++i) Result.set(i, NeededElements[i] ? DIKind::No : DIKind::Yes); return Result; } - + // Check to see which tuple elements this instruction defines. Clear them // from the set we're scanning from. auto &TheInstUse = Uses[It->second]; @@ -2838,9 +2881,9 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // If the client wants to know about super.init, check to see if we failed // it or some other element. - if (Use.FirstElement+Use.NumElements == TheMemory.NumElements && + if (Use.FirstElement + Use.NumElements == TheMemory.getNumElements() && TheMemory.isAnyDerivedClassSelf() && - Liveness.get(Liveness.size()-1) != DIKind::Yes) { + Liveness.get(Liveness.size() - 1) != DIKind::Yes) { if (SuperInitDone) *SuperInitDone = false; } @@ -2860,8 +2903,8 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // we caught it, self is no longer available. if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { if (FailedSelfUse) *FailedSelfUse = true; return false; @@ -2884,8 +2927,7 @@ static void processMemoryObject(MarkUninitializedInst *I) { DIElementUseInfo UseInfo; // Walk the use list of the pointer, collecting them into the Uses array. - collectDIElementUsesFrom(MemInfo, UseInfo, false, - /*TreatAddressToPointerAsInout*/ true); + collectDIElementUsesFrom(MemInfo, UseInfo); LifetimeChecker(MemInfo, UseInfo).doIt(); } diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp index 6da647c694476..c1c98ee765284 100644 --- a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -56,12 +56,17 @@ static bool cleanFunction(SILFunction &fn) { LLVM_FALLTHROUGH; } case BuiltinValueKind::PoundAssert: - case BuiltinValueKind::StaticReport: + case BuiltinValueKind::StaticReport: { // The call to the builtin should get removed before we reach // IRGen. - recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); + InstructionDeleter deleter; + deleter.forceDelete(bi); + // StaticReport only takes trivial operands, and therefore doesn't + // require fixing the lifetime of its operands. + deleter.cleanUpDeadInstructions(); madeChange = true; break; + } default: break; } diff --git a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp b/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp deleted file mode 100644 index 4c1f330c7942b..0000000000000 --- a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp +++ /dev/null @@ -1,143 +0,0 @@ -//===--- MarkUninitializedFixup.cpp ---------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-ownership-model-eliminator" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILVisitor.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" - -using namespace swift; - -//===----------------------------------------------------------------------===// -// Top Level Entry Point -//===----------------------------------------------------------------------===// - -static ProjectBoxInst * -getInitialProjectBox(MarkUninitializedInst *MUI, - ArrayRef Projections) { - assert(!Projections.empty()); - if (Projections.size() == 1) { - auto *PBI = Projections[0]; - assert(PBI->getParent() == MUI->getParent()); - return PBI; - } - - // Otherwise, we want to select the earliest project box. There should - // only be one. - ProjectBoxInst *PBI = Projections[0]; - - // Otherwise, we need to find the one that is closest to the - // mark_uninitialized. It should be in the same block. - for (auto *I : makeArrayRef(Projections).slice(1)) { - // If the new instruction is in a different block than the - // mark_uninitialized, it can not be a good solution, so skip it. - if (I->getParent() != MUI->getParent()) { - continue; - } - - // If PBI is not in the same block as the MUI, but I is, we picked a - // bad initial PBI, set PBI to I. - if (PBI->getParent() != MUI->getParent()) { - // Otherwise, I is a better candidate than PBI so set PBI to I. - PBI = I; - continue; - } - - // Otherwise, we have that PBI and I are both in the same block. See - // which one is first. - auto *BB = PBI->getParent(); - if (BB->end() != std::find_if(PBI->getIterator(), BB->end(), - [&I](const SILInstruction &InnerI) -> bool { - return I == &InnerI; - })) { - continue; - } - - PBI = I; - } - - assert(PBI->getParent() == MUI->getParent()); - return PBI; -} - -namespace { - -struct MarkUninitializedFixup : SILFunctionTransform { - void run() override { - // Don't rerun on deserialized functions. Nothing should have changed. - if (getFunction()->wasDeserializedCanonical()) - return; - - bool MadeChange = false; - for (auto &BB : *getFunction()) { - for (auto II = BB.begin(), IE = BB.end(); II != IE;) { - // Grab our given instruction and advance the iterator. This is - // important since we may be destroying the given instruction. - auto *MUI = dyn_cast(&*II); - ++II; - - // If we do not have a mark_uninitialized or we have a - // mark_uninitialized of an alloc_box, continue. These are not - // interesting to us. - if (!MUI) - continue; - - auto *Box = dyn_cast(MUI->getOperand()); - if (!Box) - continue; - - // We expect there to be in most cases exactly one project_box. That - // being said, it is not impossible for there to be multiple. In such - // a case, we assume that the correct project_box is the one that is - // nearest to the mark_uninitialized in the same block. This preserves - // the existing behavior. - llvm::TinyPtrVector Projections; - for (auto *Op : MUI->getUses()) { - if (auto *PBI = dyn_cast(Op->getUser())) { - Projections.push_back(PBI); - } - } - assert(!Projections.empty() && "SILGen should never emit a " - "mark_uninitialized by itself"); - - // First replace all uses of the mark_uninitialized with the box. - MUI->replaceAllUsesWith(Box); - - // That means now our project box now has the alloc_box as its - // operand. Grab that project_box. - auto *PBI = getInitialProjectBox(MUI, Projections); - - // Then create the new mark_uninitialized and force all uses of the - // project_box to go through the new mark_uninitialized. - SILBuilderWithScope B(&*std::next(PBI->getIterator())); - SILValue Undef = SILUndef::get(PBI->getType(), *PBI->getFunction()); - auto *NewMUI = - B.createMarkUninitialized(PBI->getLoc(), Undef, MUI->getKind()); - PBI->replaceAllUsesWith(NewMUI); - NewMUI->setOperand(PBI); - - // Finally, remove the old mark_uninitialized. - MUI->eraseFromParent(); - MadeChange = true; - } - } - if (MadeChange) - invalidateAnalysis( - SILAnalysis::InvalidationKind::BranchesAndInstructions); - } -}; -} // end anonymous namespace - -SILTransform *swift::createMarkUninitializedFixup() { - return new MarkUninitializedFixup(); -} diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 71493c2422ed0..3af084a808556 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -262,8 +262,7 @@ static bool shouldAttemptEvaluation(SILInstruction *inst) { SILFunction *calleeFun = apply->getCalleeFunction(); if (!calleeFun) return false; - return isKnownConstantEvaluableFunction(calleeFun) || - isConstantEvaluable(calleeFun); + return isConstantEvaluable(calleeFun); } /// Skip or evaluate the given instruction based on the evaluation policy and @@ -780,20 +779,11 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, } } -/// Collect the end points of the instructions that are data dependent on \c -/// value. A instruction is data dependent on \c value if its result may -/// transitively depends on \c value. Note that data dependencies through -/// addresses are not tracked by this function. -/// -/// \param value SILValue that is not an address. -/// \param fun SILFunction that defines \c value. -/// \param endUsers buffer for storing the found end points of the data -/// dependence chain. -static void -getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, - SmallVectorImpl &endUsers) { - assert(!value->getType().isAddress()); - +/// Given a SILValue \p value, compute the set of transitive users of the value +/// (excluding value itself) by following the use-def chain starting at value. +/// Note that this function does not follow use-def chains though branches. +static void getTransitiveUsers(SILValue value, + SmallVectorImpl &users) { // Collect the instructions that are data dependent on the value using a // fix point iteration. SmallPtrSet visitedUsers; @@ -810,17 +800,40 @@ getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, llvm::copy(user->getResults(), std::back_inserter(worklist)); } } - // At this point, visitedUsers have all the transitive, data-dependent uses. - // Compute the lifetime frontier of all the uses which are the instructions - // following the last uses. Every exit from the last uses will have a - // lifetime frontier. + users.append(visitedUsers.begin(), visitedUsers.end()); +} + +/// Collect the end points of the instructions that are data dependent on \c +/// value. A instruction is data dependent on \c value if its result may +/// transitively depends on \c value. Note that data dependencies through +/// addresses are not tracked by this function. +/// +/// \param value SILValue that is not an address. +/// \param fun SILFunction that defines \c value. +/// \param endUsers buffer for storing the found end points of the data +/// dependence chain. +static void +getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, + SmallVectorImpl &endUsers) { + assert(!value->getType().isAddress()); + + SmallVector transitiveUsers; + // Get transitive users of value, ignoring use-def chain going through + // branches. These transitive users define the end points of the constant + // evaluation. Igoring use-def chains through branches causes constant + // evaluation to miss some constant folding opportunities. This can be + // relaxed in the future, if necessary. + getTransitiveUsers(value, transitiveUsers); + + // Compute the lifetime frontier of all the transitive uses which are the + // instructions following the last uses. Every exit from the last uses will + // have a lifetime frontier. SILInstruction *valueDefinition = value->getDefiningInstruction(); SILInstruction *def = valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); ValueLifetimeAnalysis lifetimeAnalysis = - ValueLifetimeAnalysis(def, SmallVector( - visitedUsers.begin(), visitedUsers.end())); + ValueLifetimeAnalysis(def, transitiveUsers); ValueLifetimeAnalysis::Frontier frontier; bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( frontier, ValueLifetimeAnalysis::DontModifyCFG); @@ -936,7 +949,7 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, static void substituteConstants(FoldState &foldState) { ConstExprStepEvaluator &evaluator = foldState.constantEvaluator; // Instructions that are possibly dead since their results are folded. - SmallVector possiblyDeadInsts; + SmallVector possiblyDeadInsts; for (SILValue constantSILValue : foldState.getConstantSILValues()) { SymbolicValue constantSymbolicVal = @@ -980,13 +993,11 @@ static void substituteConstants(FoldState &foldState) { replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun); possiblyDeadInsts.push_back(definingInst); } - recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false, - [&](SILInstruction *DeadI) {}); } /// Check whether OSLogMessage and OSLogInterpolation instances and all their /// stored properties are constants. If not, it indicates errors that are due to -/// incorrect implementation OSLogMessage either in the overlay or in the +/// incorrect implementation of OSLogMessage either in the overlay or in the /// extensions created by users. Detect and emit diagnostics for such errors. /// The diagnostics here are for os log library authors. static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, @@ -1048,6 +1059,37 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, return errorDetected; } +/// Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed +/// to the os log call and clean up its dependencies. If the instance cannot be +/// eliminated, it implies that either the instance is not auto-generated or the +/// implementation of the os log overlay is incorrect. Therefore emit +/// diagnostics in such cases. +static void tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) { + // Collect the set of root instructions that could be dead due to constant + // folding. These include the oslogMessage initialzer call and its transitive + // users. + SmallVector oslogMessageUsers; + getTransitiveUsers(oslogMessage, oslogMessageUsers); + + InstructionDeleter deleter; + for (SILInstruction *user : oslogMessageUsers) + deleter.trackIfDead(user); + deleter.trackIfDead(oslogMessage); + + bool isOSLogMessageDead = false; + deleter.cleanUpDeadInstructions([&](SILInstruction *deadInst) { + if (deadInst == oslogMessage) + isOSLogMessageDead = true; + }); + // At this point, the OSLogMessage instance must be deleted if + // the overlay implementation (or its extensions by users) is correct. + if (!isOSLogMessageDead) { + SILFunction *fun = oslogMessage->getFunction(); + diagnose(fun->getASTContext(), oslogMessage->getLoc().getSourceLoc(), + diag::oslog_message_alive_after_opts); + } +} + /// Constant evaluate instructions starting from 'start' and fold the uses /// of the value 'oslogMessage'. Stop when oslogMessageValue is released. static bool constantFold(SILInstruction *start, @@ -1076,6 +1118,8 @@ static bool constantFold(SILInstruction *start, return false; substituteConstants(state); + + tryEliminateOSLogMessage(oslogMessage); return true; } diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 4ad37b981c35a..351068091448a 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -934,9 +934,10 @@ static SILInstruction *getNonPhiBlockIncomingValueDef(SILValue incomingValue, static bool terminatorHasAnyKnownPhis(TermInst *ti, ArrayRef insertedPhiNodesSorted) { - for (auto succArgList : ti->getSuccessorBlockArguments()) { - if (llvm::any_of(succArgList, [&](SILPhiArgument *arg) { - return binary_search(insertedPhiNodesSorted, arg); + for (auto succArgList : ti->getSuccessorBlockArgumentLists()) { + if (llvm::any_of(succArgList, [&](SILArgument *arg) { + return binary_search(insertedPhiNodesSorted, + cast(arg)); })) { return true; } @@ -2118,7 +2119,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { SILValue addr = li->getOperand(); li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } @@ -2139,7 +2140,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { SILValue addr = li->getOperand(); li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } @@ -2242,7 +2243,7 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { SILValue addr = lbi->getOperand(); lbi->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } diff --git a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp index eb4c12f4d990f..e13226fd4d6c5 100644 --- a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp +++ b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp @@ -52,14 +52,12 @@ struct SILGenCanonicalize final : CanonicalizeInstruction { SILInstruction *deadOperInst = *deadOperands.begin(); // Make sure at least the first instruction is removed from the set. deadOperands.erase(deadOperInst); - recursivelyDeleteTriviallyDeadInstructions( - deadOperInst, false, - [&](SILInstruction *deadInst) { - LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); - if (nextII == deadInst->getIterator()) - ++nextII; - deadOperands.erase(deadInst); - }); + eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) { + LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); + if (nextII == deadInst->getIterator()) + ++nextII; + deadOperands.erase(deadInst); + }); } return nextII; } diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index f75872d3dc225..9915714f5c575 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -36,10 +36,6 @@ STATISTIC(NumEliminatedInsts, "number of removed instructions"); STATISTIC(NumLoadCopyConvertedToLoadBorrow, "number of load_copy converted to load_borrow"); -//===----------------------------------------------------------------------===// -// Utility -//===----------------------------------------------------------------------===// - //===----------------------------------------------------------------------===// // Live Range Modeling //===----------------------------------------------------------------------===// @@ -296,7 +292,7 @@ struct SemanticARCOptVisitor #define FORWARDING_TERM(NAME) \ bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (auto succValues : cls->getSuccessorBlockArguments()) { \ + for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ for (SILValue v : succValues) { \ worklist.insert(v); \ } \ @@ -310,7 +306,7 @@ struct SemanticARCOptVisitor FORWARDING_TERM(CondBranch) #undef FORWARDING_TERM - bool isWrittenTo(LoadInst *li, ArrayRef destroys); + bool isWrittenTo(LoadInst *li, const LiveRange &lr); bool processWorklist(); @@ -653,12 +649,14 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // If our copy value inst has only destroy_value users, it is a dead live // range. Try to eliminate them. - if (eliminateDeadLiveRangeCopyValue(cvi)) + if (eliminateDeadLiveRangeCopyValue(cvi)) { return true; + } // Then try to perform the guaranteed copy value optimization. - if (performGuaranteedCopyValueOptimization(cvi)) + if (performGuaranteedCopyValueOptimization(cvi)) { return true; + } return false; } @@ -667,56 +665,31 @@ bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // load [copy] Optimizations //===----------------------------------------------------------------------===// -// A flow insensitive analysis that tells the load [copy] analysis if the -// storage has 0, 1, >1 writes to it. -// -// In the case of 0 writes, we return CanOptimizeLoadCopyResult::Always. -// -// In the case of 1 write, we return OnlyIfStorageIsLocal. We are taking -// advantage of definite initialization implying that an alloc_stack must be -// written to once before any loads from the memory location. Thus if we are -// local and see 1 write, we can still change to load_borrow if all other uses -// check out. -// -// If there is 2+ writes, we can not optimize = (. - -bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) { - auto *arg = cast(storage.getArgument()); - - // Then check if we have an in_guaranteed argument. In this case, we can - // always optimize load [copy] from this. - if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return false; - - // For now just return false. - return true; -} - -// Then find our accessed storage to determine whether it provides a guarantee -// for the loaded value. namespace { +/// A class that computes in a flow insensitive way if we can prove that our +/// storage is either never written to, or is initialized exactly once and never +/// written to again. In both cases, we can convert load [copy] -> load_borrow +/// safely. class StorageGuaranteesLoadVisitor : public AccessUseDefChainVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; - - // The original load instruction. - LoadInst *Load; - + + // The live range of the original load. + const LiveRange &liveRange; + // The current address being visited. SILValue currentAddress; Optional isWritten; - ArrayRef destroyValues; - public: StorageGuaranteesLoadVisitor(SemanticARCOptVisitor &arcOpt, LoadInst *load, - ArrayRef destroyValues) - : ARCOpt(arcOpt), Load(load), currentAddress(load->getOperand()), - destroyValues(destroyValues) {} + const LiveRange &liveRange) + : ARCOpt(arcOpt), liveRange(liveRange), + currentAddress(load->getOperand()) {} void answer(bool written) { currentAddress = nullptr; @@ -733,9 +706,21 @@ class StorageGuaranteesLoadVisitor } void visitArgumentAccess(SILFunctionArgument *arg) { - return answer(mayFunctionMutateArgument( - AccessedStorage(arg, AccessedStorage::Argument), - ARCOpt.F)); + // If this load_copy is from an indirect in_guaranteed argument, then we + // know for sure that it will never be written to. + if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) { + return answer(false); + } + + // TODO: This should be extended: + // + // 1. We should be able to analyze inout arguments and see if the inout + // argument is never actually written to in a flow insensitive way. + // + // 2. We should be able to analyze in arguments and see if they are only + // ever destroyed at the end of the function. In such a case, we may be + // able to also to promote load [copy] from such args to load_borrow. + return answer(true); } void visitGlobalAccess(SILValue global) { @@ -787,15 +772,11 @@ class StorageGuaranteesLoadVisitor llvm::copy(borrowInst->getUsersOfType(), std::back_inserter(baseEndBorrows)); - SmallVector valueDestroys; - llvm::copy(Load->getUsersOfType(), - std::back_inserter(valueDestroys)); - SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); // Returns true on success. So we invert. - bool foundError = - !checker.validateLifetime(baseObject, baseEndBorrows, valueDestroys); + bool foundError = !checker.validateLifetime(baseObject, baseEndBorrows, + liveRange.getDestroys()); return answer(foundError); } @@ -831,9 +812,9 @@ class StorageGuaranteesLoadVisitor SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); // Returns true on success. So we invert. - bool foundError = - !checker.validateLifetime(stack, destroyAddrs /*consuming users*/, - destroyValues /*non consuming users*/); + bool foundError = !checker.validateLifetime( + stack, destroyAddrs /*consuming users*/, + liveRange.getDestroys() /*non consuming users*/); return answer(foundError); } @@ -847,9 +828,8 @@ class StorageGuaranteesLoadVisitor } // namespace -bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load, - ArrayRef destroys) { - StorageGuaranteesLoadVisitor visitor(*this, load, destroys); +bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load, const LiveRange &lr) { + StorageGuaranteesLoadVisitor visitor(*this, load, lr); return visitor.doIt(); } @@ -873,8 +853,7 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // Then check if our address is ever written to. If it is, then we cannot use // the load_borrow because the stored value may be released during the loaded // value's live range. - auto destroyValues = lr.getDestroys(); - if (isWrittenTo(li, destroyValues)) + if (isWrittenTo(li, lr)) return false; // Ok, we can perform our optimization. Convert the load [copy] into a @@ -887,6 +866,7 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // to find the post-dominating block set of these destroy value to ensure that // we do not insert multiple end_borrow. assert(lifetimeFrontier.empty()); + auto destroyValues = lr.getDestroys(); ValueLifetimeAnalysis analysis(li, destroyValues); bool foundCriticalEdges = !analysis.computeFrontier( lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 46626b254534a..382c28bfdc48e 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -74,7 +74,6 @@ static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) { /// Passes for performing definite initialization. Must be run together in this /// order. static void addDefiniteInitialization(SILPassPipelinePlan &P) { - P.addMarkUninitializedFixup(); P.addDefiniteInitialization(); P.addRawSILInstLowering(); } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 90607e637f7d9..3aac12abe25d9 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -919,7 +919,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing // SILCombine to loop infinitely, creating and destroying the casts. - recursivelyDeleteTriviallyDeadInstructions(*Builder.getTrackingList()); + InstructionDeleter deleter; + for (SILInstruction *inst : *Builder.getTrackingList()) { + deleter.trackIfDead(inst); + } + deleter.cleanUpDeadInstructions(); Builder.getTrackingList()->clear(); return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index eee7bf3a22fe2..15810b75d66f5 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -164,11 +164,13 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) { // %addr = pointer_to_address %ptr, [strict] $T // %result = index_addr %addr, %distance // - BuiltinInst *Bytes; + BuiltinInst *Bytes = nullptr; if (match(PTAI->getOperand(), m_IndexRawPointerInst( m_ValueBase(), m_TupleExtractOperation(m_BuiltinInst(Bytes), 0)))) { + assert(Bytes != nullptr && + "Bytes should have been assigned a non-null value"); if (match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(), m_ApplyInst(BuiltinValueKind::Strideof, m_MetatypeInst(Metatype)), @@ -284,9 +286,12 @@ SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) { Builder.setCurrentDebugScope(URCI->getDebugScope()); LoadInst *load = Builder.createLoad(Loc, URCI->getSrc(), LoadOwnershipQualifier::Unqualified); - auto *cast = Builder.tryCreateUncheckedRefCast(Loc, load, - DestTy.getObjectType()); - assert(cast && "SILBuilder cannot handle reference-castable types"); + + assert(SILType::canRefCast(load->getType(), DestTy.getObjectType(), + Builder.getModule()) && + "SILBuilder cannot handle reference-castable types"); + auto *cast = Builder.createUncheckedRefCast(Loc, load, + DestTy.getObjectType()); Builder.createStore(Loc, cast, URCI->getDest(), StoreOwnershipQualifier::Unqualified); @@ -391,11 +396,12 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) { UBCI->getOperand(), UBCI->getType()); - if (auto refCast = Builder.tryCreateUncheckedRefCast( - UBCI->getLoc(), UBCI->getOperand(), UBCI->getType())) - return refCast; + if (!SILType::canRefCast(UBCI->getOperand()->getType(), UBCI->getType(), + Builder.getModule())) + return nullptr; - return nullptr; + return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(), + UBCI->getType()); } SILInstruction * diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index ddf770d2a0874..d70e8c7305d79 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -634,7 +634,7 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned"); auto *Fn = FuncBuilder.createFunction( SILLinkage::Shared, ClonedName, ClonedTy, Orig->getGenericEnvironment(), - Orig->getLocation(), Orig->isBare(), IsNotTransparent, Serialized, + Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 449bf02bd9f67..a0a827199a37c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -931,7 +931,7 @@ class ObjCMethodCall : public OutlinePattern { std::string OutlinedName; llvm::BitVector IsBridgedArgument; llvm::BitVector IsGuaranteedArgument; - BridgedReturn BridgedReturn; + ::BridgedReturn BridgedReturn; public: bool matchInstSequence(SILBasicBlock::iterator I) override; diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index d7bae9dd2a835..f688bc6921507 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -1261,7 +1261,7 @@ static bool sinkArgument(EnumCaseDataflowContext &Context, SILBasicBlock *BB, un TI->setOperand(ArgNum, CloneInst->getOperand(*DifferentOperandIndex)); // Now delete the clone as we only needed it operand. if (CloneInst != FSI) - recursivelyDeleteTriviallyDeadInstructions(CloneInst); + eliminateDeadInstruction(CloneInst); ++CloneIt; } assert(CloneIt == Clones.end() && "Clone/pred mismatch"); diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index effb3dfffa500..5c2fb108633a1 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -81,14 +81,20 @@ static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) { } // A utility function for cloning the apply instruction. -static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { +static FullApplySite CloneApply(FullApplySite AI, SILValue SelfArg, + SILBuilder &Builder) { // Clone the Apply. Builder.setCurrentDebugScope(AI.getDebugScope()); Builder.addOpenedArchetypeOperands(AI.getInstruction()); auto Args = AI.getArguments(); SmallVector Ret(Args.size()); - for (unsigned i = 0, e = Args.size(); i != e; ++i) - Ret[i] = Args[i]; + for (unsigned i = 0, e = Args.size(); i != e; ++i) { + if (i == e - 1 && SelfArg) { + Ret[i] = SelfArg; + } else { + Ret[i] = Args[i]; + } + } FullApplySite NAI; @@ -169,8 +175,8 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. - FullApplySite IdenAI = CloneApply(AI, IdenBuilder); - FullApplySite VirtAI = CloneApply(AI, VirtBuilder); + FullApplySite IdenAI = CloneApply(AI, DownCastedClassInstance, IdenBuilder); + FullApplySite VirtAI = CloneApply(AI, SILValue(), VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 9c769f5197da9..a7579edc39094 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -108,32 +108,20 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); - auto *Node = ConGraph->getNodeOrNull(ARI); - if (!Node) + auto *contentNode = ConGraph->getValueContent(ARI); + if (!contentNode) return false; // The most important check: does the object escape the current function? - if (Node->escapes()) + if (contentNode->escapes()) return false; LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI); // Collect all use-points of the allocation. These are refcount instructions // and apply instructions. - llvm::SmallVector BaseUsePoints; llvm::SmallVector UsePoints; - ConGraph->getUsePoints(Node, BaseUsePoints); - for (SILNode *UsePoint : BaseUsePoints) { - if (SILInstruction *I = dyn_cast(UsePoint)) { - UsePoints.push_back(I); - } else { - // Also block arguments can be use points. - SILBasicBlock *UseBB = cast(UsePoint)->getParent(); - // For simplicity we just add the first instruction of the block as use - // point. - UsePoints.push_back(&UseBB->front()); - } - } + ConGraph->getUsePoints(contentNode, UsePoints); ValueLifetimeAnalysis VLA(ARI, UsePoints); // Check if there is a use point before the allocation (this can happen e.g. diff --git a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp index 0050c37e36ee0..9d91a3b0b49bd 100644 --- a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp +++ b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp @@ -145,7 +145,8 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { evaluatedFunctions.insert(callee); SILModule &calleeModule = callee->getModule(); - if (callee->isAvailableExternally() && isConstantEvaluable(callee) && + if (callee->isAvailableExternally() && + hasConstantEvaluableAnnotation(callee) && callee->getOptimizationMode() != OptimizationMode::NoOptimization) { diagnose(calleeModule.getASTContext(), callee->getLocation().getSourceLoc(), @@ -161,7 +162,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { for (SILFunction &fun : *module) { // Record functions annotated as constant evaluable. - if (isConstantEvaluable(&fun)) { + if (hasConstantEvaluableAnnotation(&fun)) { constantEvaluableFunctions.insert(&fun); continue; } diff --git a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp index 3d1cf9331b697..da470ec37d705 100644 --- a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp @@ -25,6 +25,23 @@ llvm::cl::opt EnableGraphWriter( namespace { +static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn, + std::vector &Values) { + for (auto &BB : Fn) { + for (auto *Arg : BB.getArguments()) { + if (EA->isPointer(Arg)) + Values.push_back(SILValue(Arg)); + } + for (auto &II : BB) { + for (auto result : II.getResults()) { + if (EA->isPointer(result)) + Values.push_back(result); + } + } + } + return Values.size() > 1; +} + /// Dumps the escape information of all functions in the module. /// Only dumps if the compiler is built with assertions. /// For details see EscapeAnalysis. @@ -43,6 +60,33 @@ class EscapeAnalysisDumper : public SILModuleTransform { ConnectionGraph->print(llvm::outs()); if (EnableGraphWriter) ConnectionGraph->dumpCG(); + + // Gather up all Values in Fn. + std::vector Values; + if (!gatherValues(EA, F, Values)) + continue; + + // Emit the N^2 escape analysis evaluation of the values. + for (auto &bb : F) { + for (auto &ii : bb) { + if (auto fas = FullApplySite::isa(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, fas); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + if (RefCountingInst *rci = dyn_cast(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, rci); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + } + } } } #endif diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 526e2e6f07870..e3b08f8c9e458 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -140,6 +140,7 @@ void BasicBlockCloner::sinkAddressProjections() { // Because the address projections chains will be disjoint (an instruction // in one chain cannot use the result of an instruction in another chain), // the order they are sunk does not matter. + InstructionDeleter deleter; for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) { bool canSink = sinkProj.analyzeAddressProjections(&*ii); (void)canSink; @@ -150,13 +151,10 @@ void BasicBlockCloner::sinkAddressProjections() { && "canCloneInstruction should catch this."); auto nextII = std::next(ii); - recursivelyDeleteTriviallyDeadInstructions( - &*ii, false, [&nextII](SILInstruction *deadInst) { - if (deadInst->getIterator() == nextII) - ++nextII; - }); + deleter.trackIfDead(&*ii); ii = nextII; } + deleter.cleanUpDeadInstructions(); } // Populate 'projections' with the chain of address projections leading diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 13c1e0a1e9c02..3c31c4e16236c 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -86,7 +86,7 @@ deleteTriviallyDeadOperandsOfDeadArgument(MutableArrayRef termOperands, if (!i) return; op.set(SILUndef::get(op.get()->getType(), *i->getFunction())); - recursivelyDeleteTriviallyDeadInstructions(i); + eliminateDeadInstruction(i); } // Our implementation assumes that our caller is attempting to remove a dead diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index fabe4b89523cb..0d3d72b081b8a 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -2129,7 +2129,12 @@ bool swift::isKnownConstantEvaluableFunction(SILFunction *fun) { return classifyFunction(fun).hasValue(); } -bool swift::isConstantEvaluable(SILFunction *fun) { +bool swift::hasConstantEvaluableAnnotation(SILFunction *fun) { assert(fun && "fun should not be nullptr"); return fun->hasSemanticsAttr("constant_evaluable"); } + +bool swift::isConstantEvaluable(SILFunction *fun) { + return hasConstantEvaluableAnnotation(fun) || + isKnownConstantEvaluableFunction(fun); +} diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 5ca34dfc33995..af43e75cf71f2 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1696,7 +1696,6 @@ ConstantFolder::processWorkList() { // This is used to avoid duplicate error reporting in case we reach the same // instruction from different entry points in the WorkList. llvm::DenseSet ErrorSet; - llvm::SetVector FoldedUsers; CastOptimizer CastOpt(FuncBuilder, nullptr /*SILBuilderContext*/, /* replaceValueUsesAction */ [&](SILValue oldValue, SILValue newValue) { @@ -1751,7 +1750,7 @@ ConstantFolder::processWorkList() { // Schedule users for constant folding. WorkList.insert(AssertConfInt); // Delete the call. - recursivelyDeleteTriviallyDeadInstructions(BI); + eliminateDeadInstruction(BI); InvalidateInstructions = true; continue; @@ -1819,9 +1818,8 @@ ConstantFolder::processWorkList() { if (constantFoldGlobalStringTablePointerBuiltin(cast(I), EnableDiagnostics)) { // Here, the bulitin instruction got folded, so clean it up. - recursivelyDeleteTriviallyDeadInstructions( - I, /*force*/ true, - [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); + eliminateDeadInstruction( + I, [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); InvalidateInstructions = true; } continue; @@ -1873,7 +1871,7 @@ ConstantFolder::processWorkList() { } // Go through all users of the constant and try to fold them. - FoldedUsers.clear(); + InstructionDeleter deleter; for (auto Result : I->getResults()) { for (auto *Use : Result->getUses()) { SILInstruction *User = Use->getUser(); @@ -1897,7 +1895,7 @@ ConstantFolder::processWorkList() { // this as part of the constant folding logic, because there is no value // they can produce (other than empty tuple, which is wasteful). if (isa(User)) - FoldedUsers.insert(User); + deleter.trackIfDead(User); // See if we have an instruction that is read none and has a stateless // inverse. If we do, add it to the worklist so we can check its users @@ -1963,10 +1961,7 @@ ConstantFolder::processWorkList() { if (C->getDefiningInstruction() == User) continue; - // Ok, we have succeeded. Add user to the FoldedUsers list and perform - // the necessary cleanups, RAUWs, etc. - FoldedUsers.insert(User); - InvalidateInstructions = true; + // Ok, we have succeeded. ++NumInstFolded; // We were able to fold, so all users should use the new folded @@ -2005,11 +2000,16 @@ ConstantFolder::processWorkList() { // In contrast, if we realize that RAUWing %3 does nothing and skip // it, we exit the worklist as expected. SILValue r = User->getResult(Index); - if (r->use_empty()) + if (r->use_empty()) { + deleter.trackIfDead(User); continue; + } // Otherwise, do the RAUW. User->getResult(Index)->replaceAllUsesWith(C); + // Record the user if it is dead to perform the necessary cleanups + // later. + deleter.trackIfDead(User); // The new constant could be further folded now, add it to the // worklist. @@ -2018,18 +2018,12 @@ ConstantFolder::processWorkList() { } } } - // Eagerly DCE. We do this after visiting all users to ensure we don't // invalidate the uses iterator. - ArrayRef UserArray = FoldedUsers.getArrayRef(); - if (!UserArray.empty()) { + deleter.cleanUpDeadInstructions([&](SILInstruction *DeadI) { + WorkList.remove(DeadI); InvalidateInstructions = true; - } - - recursivelyDeleteTriviallyDeadInstructions(UserArray, false, - [&](SILInstruction *DeadI) { - WorkList.remove(DeadI); - }); + }); } // TODO: refactor this code outside of the method. Passes should not merge diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 9c97afaa3eb43..f97e107514edb 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -12,8 +12,8 @@ #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/SubstitutionMap.h" #include "swift/AST/SemanticAttrs.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/DynamicCasts.h" @@ -27,6 +27,7 @@ #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/ConstExpr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSwitch.h" @@ -171,9 +172,283 @@ bool swift::isIntermediateRelease(SILInstruction *inst, return false; } +static bool hasOnlyEndOfScopeOrDestroyUses(SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + bool isDebugUser = user->isDebugInstruction(); + if (!isa(user) && !isEndOfScopeMarker(user) && + !isDebugUser) + return false; + // Include debug uses only in Onone mode. + if (isDebugUser && inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization) + return false; + } + } + return true; +} + +/// Return true iff the \p applySite calls a constant evaluable function and if +/// it is read-only which implies the following: +/// (1) The call does not write into any memory location. +/// (2) The call may destroy owned parameters i.e., consume them. +/// (3) The call does not throw or exit the program. +static bool isReadOnlyConstantEvaluableCall(FullApplySite applySite) { + assert(applySite); + SILFunction *callee = applySite.getCalleeFunction(); + if (!callee || !isConstantEvaluable(callee)) { + return false; + } + // Here all effects of the call is restricted to its indirect results, which + // must have value semantics. If there are no indirect results, the call must + // be read-only, except for consuming its operands. + return applySite.getNumIndirectSILResults() == 0; +} + +/// A scope-affecting instruction is an instruction which may end the scope of +/// its operand or may produce scoped results that require cleaning up. E.g. +/// begin_borrow, begin_access, copy_value, a call that produces a owned value +/// are scoped instructions. The scope of the results of the first two +/// instructions end with an end_borrow/acess instruction, while those of the +/// latter two end with a consuming operation like destroy_value instruction. +/// These instruction may also end the scope of its operand e.g. a call could +/// consume owned arguments thereby ending its scope. Dead-code eliminating a +/// scope-affecting instruction requires fixing the lifetime of the non-trivial +/// operands of the instruction and requires cleaning up the end-of-scope uses +/// of non-trivial results. +/// +/// \param inst instruction that checked for liveness. +static bool isScopeAffectingInstructionDead(SILInstruction *inst) { + SILFunction *fun = inst->getFunction(); + assert(fun && "Instruction has no function."); + // Only support ownership SIL for scoped instructions. + if (!fun->hasOwnership()) { + return false; + } + // If the instruction has any use other than end of scope use or destroy_value + // use, bail out. + if (!hasOnlyEndOfScopeOrDestroyUses(inst)) { + return false; + } + // If inst is a copy or beginning of scope, inst is dead, since we know that + // it is used only in a destroy_value or end-of-scope instruction. + if (getSingleValueCopyOrCast(inst)) + return true; + + switch (inst->getKind()) { + case SILInstructionKind::LoadBorrowInst: { + // A load_borrow only used in an end_borrow is dead. + return true; + } + case SILInstructionKind::LoadInst: { + LoadOwnershipQualifier loadOwnershipQual = + cast(inst)->getOwnershipQualifier(); + // If the load creates a copy, it is dead, since we know that if at all it + // is used, it is only in a destroy_value instruction. + return (loadOwnershipQual == LoadOwnershipQualifier::Copy || + loadOwnershipQual == LoadOwnershipQualifier::Trivial); + // TODO: we can handle load [take] but we would have to know that the + // operand has been consumed. Note that OperandOwnershipKind map does not + // say this for load. + } + case SILInstructionKind::PartialApplyInst: { + // Partial applies that are only used in destroys cannot have any effect on + // the program state, provided the values they capture are explicitly + // destroyed. + return true; + } + case SILInstructionKind::StructInst: + case SILInstructionKind::EnumInst: + case SILInstructionKind::TupleInst: + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::DestructureStructInst: + case SILInstructionKind::DestructureTupleInst: { + // All these ownership forwarding instructions that are only used in + // destroys are dead provided the values they consume are destroyed + // explicitly. + return true; + } + case SILInstructionKind::ApplyInst: { + // Given a call to a function annotated as constant_evaluable, the call + // will be removed as long as the following holds: + // 1. If the call destroys its arguments: when removing the call, a destroy + // of each argument is added. + // 2. If the call returns a dead value i.e., a value that is only + // destroyed: both the call and corresponding destroy will be removed. + // Note that a value returned by a constant evaluable function must either + // contain constant pure values (trivial instances or array/string + // literals), or the arguments passed to the call. Given that its arguments + // will be destroyed explicitly, it is okay to remove the destroys of the + // return value of a constant evaluable function. + FullApplySite applySite(cast(inst)); + return isReadOnlyConstantEvaluableCall(applySite); + } + default: { + return false; + } + } +} + +void InstructionDeleter::trackIfDead(SILInstruction *inst) { + if (isInstructionTriviallyDead(inst) || + isScopeAffectingInstructionDead(inst)) { + assert(!isIncidentalUse(inst) && !isa(inst) && + "Incidental uses cannot be removed in isolation. " + "They would be removed iff the operand is dead"); + deadInstructions.insert(inst); + } +} + +/// Given an \p operand that belongs to an instruction that will be removed, +/// destroy the operand just before the instruction, if the instruction consumes +/// \p operand. This function will result in a double consume, which is expected +/// to be resolved when the caller deletes the original instruction. This +/// function works only on ownership SIL. +static void destroyConsumedOperandOfDeadInst(Operand &operand) { + assert(operand.get() && operand.getUser()); + SILInstruction *deadInst = operand.getUser(); + SILFunction *fun = deadInst->getFunction(); + assert(fun->hasOwnership()); + + SILValue operandValue = operand.get(); + if (operandValue->getType().isTrivial(*fun)) + return; + // A scope ending instruction cannot be deleted in isolation without removing + // the instruction defining its operand as well. + assert(!isEndOfScopeMarker(deadInst) && !isa(deadInst) && + "lifetime ending instruction is deleted without its operand"); + ValueOwnershipKind operandOwnershipKind = operandValue.getOwnershipKind(); + UseLifetimeConstraint lifetimeConstraint = + operand.getOwnershipKindMap().getLifetimeConstraint(operandOwnershipKind); + if (lifetimeConstraint == UseLifetimeConstraint::MustBeInvalidated) { + // Since deadInst cannot be an end-of-scope instruction (asserted above), + // this must be a consuming use of an owned value. + assert(operandOwnershipKind == ValueOwnershipKind::Owned); + SILBuilderWithScope builder(deadInst); + builder.emitDestroyValueOperation(deadInst->getLoc(), operandValue); + } +} + namespace { using CallbackTy = llvm::function_ref; -} // end anonymous namespace +} // namespace + +void InstructionDeleter::deleteInstruction(SILInstruction *inst, + CallbackTy callback, + bool fixOperandLifetimes) { + // We cannot fix operand lifetimes in non-ownership SIL. + assert(!fixOperandLifetimes || inst->getFunction()->hasOwnership()); + // Collect instruction and its immediate uses and check if they are all + // incidental uses. Also, invoke the callback on the instruction and its uses. + // Note that the Callback is invoked before deleting anything to ensure that + // the SIL is valid at the time of the callback. + SmallVector toDeleteInsts; + toDeleteInsts.push_back(inst); + callback(inst); + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + assert(isIncidentalUse(user) || isa(user)); + callback(user); + toDeleteInsts.push_back(user); + } + } + // Record definitions of instruction's operands. Also, in case an operand is + // consumed by inst, emit necessary compensation code. + SmallVector operandDefinitions; + for (Operand &operand : inst->getAllOperands()) { + SILValue operandValue = operand.get(); + assert(operandValue && + "Instruction's operand are deleted before the instruction"); + SILInstruction *defInst = operandValue->getDefiningInstruction(); + // If the operand has a defining instruction, it could be potentially + // dead. Therefore, record the definition. + if (defInst) + operandDefinitions.push_back(defInst); + // The scope of the operand could be ended by inst. Therefore, emit + // any compensating code needed to end the scope of the operand value + // once inst is deleted. + if (fixOperandLifetimes) + destroyConsumedOperandOfDeadInst(operand); + } + // First drop all references from all instructions to be deleted and then + // erase the instruction. Note that this is done in this order so that when an + // instruction is deleted, its uses would have dropped their references. + for (SILInstruction *inst : toDeleteInsts) { + inst->dropAllReferences(); + } + for (SILInstruction *inst : toDeleteInsts) { + inst->eraseFromParent(); + } + // Record operand definitions that become dead now. + for (SILInstruction *operandValInst : operandDefinitions) { + trackIfDead(operandValInst); + } +} + +void InstructionDeleter::cleanUpDeadInstructions(CallbackTy callback) { + SILFunction *fun = nullptr; + if (!deadInstructions.empty()) + fun = deadInstructions.front()->getFunction(); + while (!deadInstructions.empty()) { + SmallVector currentDeadInsts(deadInstructions.begin(), + deadInstructions.end()); + // Though deadInstructions is cleared here, calls to deleteInstruction may + // append to deadInstructions. So we need to iterate until this it is empty. + deadInstructions.clear(); + for (SILInstruction *deadInst : currentDeadInsts) { + // deadInst will not have been deleted in the previous iterations, + // because, by definition, deleteInstruction will only delete an earlier + // instruction and its incidental/destroy uses. The former cannot be + // deadInst as deadInstructions is a set vector, and the latter cannot be + // in deadInstructions as they are incidental uses which are never added + // to deadInstructions. + deleteInstruction(deadInst, callback, /*Fix lifetime of operands*/ + fun->hasOwnership()); + } + } +} + +static bool hasOnlyIncidentalUses(SILInstruction *inst, + bool disallowDebugUses = false) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + if (!isIncidentalUse(user)) + return false; + if (disallowDebugUses && user->isDebugInstruction()) + return false; + } + } + return true; +} + +void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst, + CallbackTy callback) { + SILFunction *fun = inst->getFunction(); + assert(fun->hasOwnership()); + bool disallowDebugUses = + fun->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ true); +} + +void InstructionDeleter::forceDelete(SILInstruction *inst, + CallbackTy callback) { + bool disallowDebugUses = + inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ false); +} + +void swift::eliminateDeadInstruction(SILInstruction *inst, + CallbackTy callback) { + InstructionDeleter deleter; + deleter.trackIfDead(inst); + deleter.cleanUpDeadInstructions(callback); +} void swift::recursivelyDeleteTriviallyDeadInstructions( ArrayRef ia, bool force, CallbackTy callback) { @@ -205,8 +480,8 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( // If the operand is an instruction that is only used by the instruction // being deleted, delete it. if (auto *operandValInst = operandVal->getDefiningInstruction()) - if (!deadInsts.count(operandValInst) - && isInstructionTriviallyDead(operandValInst)) + if (!deadInsts.count(operandValInst) && + isInstructionTriviallyDead(operandValInst)) nextInsts.insert(operandValInst); } @@ -1020,7 +1295,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, // possible for that value. // If we have qualified ownership, we should just emit a destroy value. - if (arg->getFunction()->hasOwnership()) { + if (builder.getFunction().hasOwnership()) { callbacks.createdNewInst(builder.createDestroyValue(loc, arg)); return; } diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 33e351a1f851b..be601b31c226d 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "SolutionResult.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -487,44 +488,143 @@ class BuilderClosureVisitor } // end anonymous namespace -BraceStmt * -TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType) { - // Try to build a single result expression. - auto &ctx = FD->getASTContext(); - BuilderClosureVisitor visitor(ctx, nullptr, - /*wantExpr=*/true, builderType); - Expr *returnExpr = visitor.visit(body); - if (!returnExpr) - return nullptr; +/// Find the return statements in the given body, which block the application +/// of a function builder. +static std::vector findReturnStatements(AnyFunctionRef fn); - // Make sure we have a usable result type for the body. - Type returnType = AnyFunctionRef(FD).getBodyResultType(); - if (!returnType || returnType->hasError()) +Optional TypeChecker::applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType) { + // Pre-check the body: pre-check any expressions in it and look + // for return statements. + // + // If we encountered an error or there was an explicit result type, + // bail out and report that to the caller. + auto &ctx = func->getASTContext(); + auto request = PreCheckFunctionBuilderRequest{func}; + switch (evaluateOrDefault( + ctx.evaluator, request, FunctionBuilderClosurePreCheck::Error)) { + case FunctionBuilderClosurePreCheck::Okay: + // If the pre-check was okay, apply the function-builder transform. + break; + + case FunctionBuilderClosurePreCheck::Error: return nullptr; - auto loc = returnExpr->getStartLoc(); - auto returnStmt = new (ctx) ReturnStmt(loc, returnExpr, /*implicit*/ true); - return BraceStmt::create(ctx, body->getLBraceLoc(), { returnStmt }, - body->getRBraceLoc()); + case FunctionBuilderClosurePreCheck::HasReturnStmt: { + // One or more explicit 'return' statements were encountered, which + // disables the function builder transform. Warn when we do this. + auto returnStmts = findReturnStatements(func); + assert(!returnStmts.empty()); + + ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_disabled_by_return, builderType); + + // Note that one can remove the function builder attribute. + auto attr = func->getAttachedFunctionBuilder(); + if (!attr) { + if (auto accessor = dyn_cast(func)) { + attr = accessor->getStorage()->getAttachedFunctionBuilder(); + } + } + + if (attr) { + ctx.Diags.diagnose( + attr->getLocation(), diag::function_builder_remove_attr) + .fixItRemove(attr->getRangeWithAt()); + attr->setInvalid(); + } + + // Note that one can remove all of the return statements. + { + auto diag = ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_remove_returns); + for (auto returnStmt : returnStmts) { + diag.fixItRemove(returnStmt->getReturnLoc()); + } + } + + return None; + } + } + + ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes; + auto resultInterfaceTy = func->getResultInterfaceType(); + auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy); + + // Determine whether we're inferring the underlying type for the opaque + // result type of this function. + ConstraintKind resultConstraintKind = ConstraintKind::Conversion; + if (auto opaque = resultContextType->getAs()) { + if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) { + options |= ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType; + resultConstraintKind = ConstraintKind::OpaqueUnderlyingType; + } + } + + // Build a constraint system in which we can check the body of the function. + ConstraintSystem cs(func, options); + + // FIXME: check the result + cs.matchFunctionBuilder(func, builderType, resultContextType, + resultConstraintKind, + /*calleeLocator=*/nullptr, + /*FIXME:*/ConstraintLocatorBuilder(nullptr)); + + // Solve the constraint system. + SmallVector solutions; + if (cs.solve(solutions) || solutions.size() != 1) { + // Try to fix the system or provide a decent diagnostic. + auto salvagedResult = cs.salvage(); + switch (salvagedResult.getKind()) { + case SolutionResult::Kind::Success: + solutions.clear(); + solutions.push_back(std::move(salvagedResult).takeSolution()); + break; + + case SolutionResult::Kind::Error: + case SolutionResult::Kind::Ambiguous: + return nullptr; + + case SolutionResult::Kind::UndiagnosedError: + cs.diagnoseFailureFor(SolutionApplicationTarget(func)); + salvagedResult.markAsDiagnosed(); + return nullptr; + + case SolutionResult::Kind::TooComplex: + func->diagnose(diag::expression_too_complex) + .highlight(func->getBodySourceRange()); + salvagedResult.markAsDiagnosed(); + return nullptr; + } + + // The system was salvaged; continue on as if nothing happened. + } + + // Apply the solution to the function body. + return cast_or_null( + cs.applySolutionToBody(solutions.front(), func)); } -ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( - ClosureExpr *closure, Type builderType, ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator) { +ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad function builder type"); assert(builder->getAttrs().hasAttribute()); // FIXME: Right now, single-expression closures suppress the function // builder translation. - if (closure->hasSingleExpressionBody()) - return getTypeMatchSuccess(); + if (auto closure = fn.getAbstractClosureExpr()) { + if (closure->hasSingleExpressionBody()) + return getTypeMatchSuccess(); + } - // Pre-check the closure body: pre-check any expressions in it and look + // Pre-check the body: pre-check any expressions in it and look // for return statements. - auto request = PreCheckFunctionBuilderRequest{closure}; + auto request = PreCheckFunctionBuilderRequest{fn}; switch (evaluateOrDefault(getASTContext().evaluator, request, FunctionBuilderClosurePreCheck::Error)) { case FunctionBuilderClosurePreCheck::Okay: @@ -547,7 +647,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( // Check whether we can apply this specific function builder. BuilderClosureVisitor visitor(getASTContext(), this, /*wantExpr=*/false, builderType); - (void)visitor.visit(closure->getBody()); + (void)visitor.visit(fn.getBody()); // If we saw a control-flow statement or declaration that the builder // cannot handle, we don't have a well-formed function builder application. @@ -585,17 +685,18 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( BuilderClosureVisitor visitor(getASTContext(), this, /*wantExpr=*/true, builderType); - Expr *singleExpr = visitor.visit(closure->getBody()); + Expr *singleExpr = visitor.visit(fn.getBody()); // We've already pre-checked all the original expressions, but do the // pre-check to the generated expression just to set up any preconditions // that CSGen might have. // // TODO: just build the AST the way we want it in the first place. - if (ConstraintSystem::preCheckExpression(singleExpr, closure)) + auto dc = fn.getAsDeclContext(); + if (ConstraintSystem::preCheckExpression(singleExpr, dc)) return getTypeMatchFailure(locator); - singleExpr = generateConstraints(singleExpr, closure); + singleExpr = generateConstraints(singleExpr, dc); if (!singleExpr) return getTypeMatchFailure(locator); @@ -604,21 +705,19 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( // Record the transformation. assert(std::find_if( - builderTransformedClosures.begin(), - builderTransformedClosures.end(), - [&](const std::pair &elt) { - return elt.first == closure; - }) == builderTransformedClosures.end() && + functionBuilderTransformed.begin(), + functionBuilderTransformed.end(), + [&](const std::pair &elt) { + return elt.first == fn; + }) == functionBuilderTransformed.end() && "already transformed this closure along this path!?!"); - builderTransformedClosures.push_back( - std::make_pair(closure, - AppliedBuilderTransform{builderType, singleExpr})); - - // Bind the result type of the closure to the type of the transformed - // expression. - Type closureType = getType(closure); - auto fnType = closureType->castTo(); - addConstraint(ConstraintKind::Equal, fnType->getResult(), transformedType, + functionBuilderTransformed.push_back( + std::make_pair( + fn, + AppliedBuilderTransform{builderType, singleExpr, bodyResultType})); + + // Bind the body result type to the type of the transformed expression. + addConstraint(bodyResultConstraintKind, transformedType, bodyResultType, locator); return getTypeMatchSuccess(); } @@ -626,28 +725,33 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( namespace { /// Pre-check all the expressions in the closure body. -class PreCheckFunctionBuilderClosure : public ASTWalker { - ClosureExpr *Closure; - bool HasReturnStmt = false; +class PreCheckFunctionBuilderApplication : public ASTWalker { + AnyFunctionRef Fn; + bool SkipPrecheck = false; + std::vector ReturnStmts; bool HasError = false; + + bool hasReturnStmt() const { return !ReturnStmts.empty(); } + public: - PreCheckFunctionBuilderClosure(ClosureExpr *closure) - : Closure(closure) {} + PreCheckFunctionBuilderApplication(AnyFunctionRef fn, bool skipPrecheck) + : Fn(fn), SkipPrecheck(skipPrecheck) {} + + const std::vector getReturnStmts() const { return ReturnStmts; } FunctionBuilderClosurePreCheck run() { - Stmt *oldBody = Closure->getBody(); + Stmt *oldBody = Fn.getBody(); Stmt *newBody = oldBody->walk(*this); // If the walk was aborted, it was because we had a problem of some kind. - assert((newBody == nullptr) == (HasError || HasReturnStmt) && - "unexpected short-circuit while walking closure body"); - if (!newBody) { - if (HasError) - return FunctionBuilderClosurePreCheck::Error; + assert((newBody == nullptr) == HasError && + "unexpected short-circuit while walking body"); + if (HasError) + return FunctionBuilderClosurePreCheck::Error; + if (hasReturnStmt()) return FunctionBuilderClosurePreCheck::HasReturnStmt; - } assert(oldBody == newBody && "pre-check walk wasn't in-place?"); @@ -658,7 +762,8 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { // Pre-check the expression. If this fails, abort the walk immediately. // Otherwise, replace the expression with the result of pre-checking. // In either case, don't recurse into the expression. - if (ConstraintSystem::preCheckExpression(E, /*DC*/ Closure)) { + if (!SkipPrecheck && + ConstraintSystem::preCheckExpression(E, /*DC*/ Fn.getAsDeclContext())) { HasError = true; return std::make_pair(false, nullptr); } @@ -667,10 +772,12 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { } std::pair walkToStmtPre(Stmt *S) override { - // If we see a return statement, abort the walk immediately. - if (isa(S)) { - HasReturnStmt = true; - return std::make_pair(false, nullptr); + // If we see a return statement, note it.. + if (auto returnStmt = dyn_cast(S)) { + if (!returnStmt->isImplicit()) { + ReturnStmts.push_back(returnStmt); + return std::make_pair(false, S); + } } // Otherwise, recurse into the statement normally. @@ -682,10 +789,18 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { llvm::Expected PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, - ClosureExpr *closure) const { + AnyFunctionRef fn) const { // Single-expression closures should already have been pre-checked. - if (closure->hasSingleExpressionBody()) - return FunctionBuilderClosurePreCheck::Okay; + if (auto closure = fn.getAbstractClosureExpr()) { + if (closure->hasSingleExpressionBody()) + return FunctionBuilderClosurePreCheck::Okay; + } + + return PreCheckFunctionBuilderApplication(fn, false).run(); +} - return PreCheckFunctionBuilderClosure(closure).run(); +std::vector findReturnStatements(AnyFunctionRef fn) { + PreCheckFunctionBuilderApplication precheck(fn, true); + (void)precheck.run(); + return precheck.getReturnStmts(); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 21e74782f3db7..90d0af6790a58 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -26,6 +26,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -86,6 +87,7 @@ Solution::computeSubstitutions(GenericSignature sig, return ProtocolConformanceRef(protoType); } + // FIXME: Retrieve the conformance from the solution itself. return TypeChecker::conformsToProtocol(replacement, protoType, getConstraintSystem().DC, ConformanceCheckFlags::InExpression); @@ -378,6 +380,66 @@ namespace { return base.getOldType(); } + // Returns None if the AST does not contain enough information to recover + // substitutions; this is different from an Optional(SubstitutionMap()), + // indicating a valid call to a non-generic operator. + Optional + getOperatorSubstitutions(ValueDecl *witness, Type refType) { + // We have to recover substitutions in this hacky way because + // the AST does not retain enough information to devirtualize + // calls like this. + auto witnessType = witness->getInterfaceType(); + + // Compute the substitutions. + auto *gft = witnessType->getAs(); + if (gft == nullptr) { + if (refType->isEqual(witnessType)) + return SubstitutionMap(); + return None; + } + + auto sig = gft->getGenericSignature(); + auto *env = sig->getGenericEnvironment(); + + witnessType = FunctionType::get(gft->getParams(), + gft->getResult(), + gft->getExtInfo()); + witnessType = env->mapTypeIntoContext(witnessType); + + TypeSubstitutionMap subs; + auto substType = witnessType->substituteBindingsTo( + refType, + [&](ArchetypeType *origType, CanType substType) -> CanType { + if (auto gpType = dyn_cast( + origType->getInterfaceType()->getCanonicalType())) + subs[gpType] = substType; + + return substType; + }); + + // If substitution failed, it means that the protocol requirement type + // and the witness type did not match up. The only time that this + // should happen is when the witness is defined in a base class and + // the actual call uses a derived class. For example, + // + // protocol P { func +(lhs: Self, rhs: Self) } + // class Base : P { func +(lhs: Base, rhs: Base) {} } + // class Derived : Base {} + // + // If we enter this code path with two operands of type Derived, + // we know we're calling the protocol requirement P.+, with a + // substituted type of (Derived, Derived) -> (). But the type of + // the witness is (Base, Base) -> (). Just bail out and make a + // witness method call in this rare case; SIL mandatory optimizations + // will likely devirtualize it anyway. + if (!substType) + return None; + + return SubstitutionMap::get(sig, + QueryTypeSubstitutionMap{subs}, + TypeChecker::LookUpConformance(cs.DC)); + } + public: /// Build a reference to the given declaration. Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc, @@ -400,56 +462,53 @@ namespace { // Handle operator requirements found in protocols. if (auto proto = dyn_cast(decl->getDeclContext())) { - // If we don't have an archetype or existential, we have to call the - // witness. + // If we have a concrete conformance, build a call to the witness. + // // FIXME: This is awful. We should be able to handle this as a call to // the protocol requirement with Self == the concrete type, and SILGen // (or later) can devirtualize as appropriate. - if (!baseTy->is() && !baseTy->isAnyExistentialType()) { - auto conformance = - TypeChecker::conformsToProtocol( - baseTy, proto, cs.DC, - ConformanceCheckFlags::InExpression); - if (conformance.isConcrete()) { - if (auto witness = - conformance.getConcrete()->getWitnessDecl(decl)) { - // Hack up an AST that we can type-check (independently) to get - // it into the right form. - // FIXME: the hop through 'getDecl()' is because - // SpecializedProtocolConformance doesn't substitute into - // witnesses' ConcreteDeclRefs. - Type expectedFnType = simplifyType(overload.openedType); - assert(expectedFnType->isEqual( - fullType->castTo()->getResult()) && - "Cannot handle adjustments made to the opened type"); + auto conformance = + TypeChecker::conformsToProtocol( + baseTy, proto, cs.DC, + ConformanceCheckFlags::InExpression); + if (conformance.isConcrete()) { + if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) { + // The fullType was computed by substituting the protocol + // requirement so it always has a (Self) -> ... curried + // application. Strip it off if the witness was a top-level + // function. + Type refType; + if (witness->getDeclContext()->isTypeContext()) + refType = fullType; + else + refType = fullType->castTo()->getResult(); + + // Build the AST for the call to the witness. + auto subMap = getOperatorSubstitutions(witness, refType); + if (subMap) { + ConcreteDeclRef witnessRef(witness, *subMap); + auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc, + /*Implicit=*/false); + declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); + cs.setType(declRefExpr, refType); + Expr *refExpr; if (witness->getDeclContext()->isTypeContext()) { + // If the operator is a type member, add the implicit + // (Self) -> ... call. Expr *base = TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx); - refExpr = new (ctx) MemberRefExpr(base, SourceLoc(), witness, - loc, /*Implicit=*/true); + cs.setType(base, MetatypeType::get(baseTy)); + + refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr, + SourceLoc(), base); + auto refType = fullType->castTo()->getResult(); + cs.setType(refExpr, refType); } else { - auto declRefExpr = new (ctx) DeclRefExpr(witness, loc, - /*Implicit=*/false); - declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); refExpr = declRefExpr; } - auto resultTy = TypeChecker::typeCheckExpression( - refExpr, cs.DC, TypeLoc::withoutLoc(expectedFnType), - CTP_CannotFail); - if (!resultTy) - return nullptr; - - cs.cacheExprTypes(refExpr); - - // Remove an outer function-conversion expression. This - // happens when we end up referring to a witness for a - // superclass conformance, and 'Self' differs. - if (auto fnConv = dyn_cast(refExpr)) - refExpr = fnConv->getSubExpr(); - return forceUnwrapIfExpected(refExpr, choice, locator); } } @@ -999,7 +1058,7 @@ namespace { } } - return finishApply(apply, memberRef, openedType, locator); + return finishApply(apply, openedType, locator, memberLocator); } /// Convert the given literal expression via a protocol pair. @@ -1055,15 +1114,16 @@ namespace { /// \param apply The function application to finish type-checking, which /// may be a newly-built expression. /// - /// \param callee The callee for the function being applied. - /// /// \param openedType The "opened" type this expression had during /// type checking, which will be used to specialize the resulting, /// type-checked expression appropriately. /// /// \param locator The locator for the original expression. - Expr *finishApply(ApplyExpr *apply, ConcreteDeclRef callee, Type openedType, - ConstraintLocatorBuilder locator); + /// + /// \param calleeLocator The locator that identifies the apply's callee. + Expr *finishApply(ApplyExpr *apply, Type openedType, + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator); // Resolve `@dynamicCallable` applications. Expr *finishApplyDynamicCallable(ApplyExpr *apply, @@ -1170,41 +1230,13 @@ namespace { bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics, - Optional selected = None) { - - // Determine the declaration selected for this subscript operation. - if (!selected) - selected = solution.getOverloadChoiceIfAvailable( - cs.getConstraintLocator( - locator.withPathElement( - ConstraintLocator::SubscriptMember))); - - // Handles situation where there was a solution available but it didn't - // have a proper overload selected from subscript call, might be because - // solver was allowed to return free or unresolved types, which can - // happen while running diagnostics on one of the expressions. - if (!selected.hasValue()) { - auto &de = cs.getASTContext().Diags; - auto baseType = cs.getType(base); - - if (auto errorType = baseType->getAs()) { - de.diagnose(base->getLoc(), diag::cannot_subscript_base, - errorType->getOriginalType()) - .highlight(base->getSourceRange()); - } else { - de.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) - .highlight(base->getSourceRange()); - } - - return nullptr; - } - + const SelectedOverload &selected) { // Build the new subscript. auto newSubscript = buildSubscriptHelper(base, index, argLabels, - *selected, hasTrailingClosure, + selected, hasTrailingClosure, locator, isImplicit, semantics); - if (selected->choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { + if (selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { // Rewrite for implicit unwrapping if the solution requires it. auto *dynamicLocator = cs.getConstraintLocator( locator, {ConstraintLocator::SubscriptMember, @@ -1219,19 +1251,19 @@ namespace { } } - if (selected->choice.isDecl()) { + if (selected.choice.isDecl()) { auto locatorKind = ConstraintLocator::SubscriptMember; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::DynamicMemberLookup) locatorKind = ConstraintLocator::Member; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup && !isa(locator.getAnchor())) locatorKind = ConstraintLocator::Member; newSubscript = - forceUnwrapIfExpected(newSubscript, selected->choice, + forceUnwrapIfExpected(newSubscript, selected.choice, locator.withPathElement(locatorKind)); } @@ -1240,7 +1272,7 @@ namespace { Expr *buildSubscriptHelper(Expr *base, Expr *index, ArrayRef argLabels, - SelectedOverload &selected, + const SelectedOverload &selected, bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics) { @@ -2360,17 +2392,16 @@ namespace { // If there was an argument, apply it. if (auto arg = expr->getArgument()) { - // Find the callee. Note this may be different to the member being - // referenced for things like callAsFunction. + // Get the callee locator. Note this may be different to the locator for + // the member being referenced for things like callAsFunction. auto *calleeLoc = cs.getCalleeLocator(exprLoc); - auto calleeOverload = solution.getOverloadChoice(calleeLoc); - auto callee = resolveConcreteDeclRef(calleeOverload.choice.getDecl(), - calleeLoc); + + // Build and finish the apply. ApplyExpr *apply = CallExpr::create( ctx, result, arg, expr->getArgumentLabels(), expr->getArgumentLabelLocs(), expr->hasTrailingClosure(), /*implicit=*/expr->isImplicit(), Type(), getType); - result = finishApply(apply, callee, Type(), exprLoc); + result = finishApply(apply, Type(), exprLoc, calleeLoc); // FIXME: Application could fail, because some of the solutions // are not expressible in AST (yet?), like certain tuple-to-tuple @@ -2549,9 +2580,8 @@ namespace { auto *call = new (cs.getASTContext()) DotSyntaxCallExpr(ctorRef, dotLoc, base); - return finishApply(call, callee, cs.getType(expr), - ConstraintLocatorBuilder( - cs.getConstraintLocator(expr))); + return finishApply(call, cs.getType(expr), cs.getConstraintLocator(expr), + ctorLocator); } Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc, @@ -2804,8 +2834,29 @@ namespace { cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); auto overload = solution.getOverloadChoiceIfAvailable(memberLocator); - if (overload && overload->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup) { + // Handles situation where there was a solution available but it didn't + // have a proper overload selected from subscript call, might be because + // solver was allowed to return free or unresolved types, which can + // happen while running diagnostics on one of the expressions. + if (!overload) { + const auto *base = expr->getBase(); + auto &de = cs.getASTContext().Diags; + auto baseType = cs.getType(base); + + if (auto errorType = baseType->getAs()) { + de.diagnose(base->getLoc(), diag::cannot_subscript_base, + errorType->getOriginalType()) + .highlight(base->getSourceRange()); + } else { + de.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) + .highlight(base->getSourceRange()); + } + + return nullptr; + } + + if (overload->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup) { return buildDynamicMemberLookupRef( expr, expr->getBase(), expr->getIndex()->getStartLoc(), SourceLoc(), *overload, memberLocator); @@ -2814,7 +2865,7 @@ namespace { return buildSubscript( expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), expr->getAccessSemantics(), overload); + expr->isImplicit(), expr->getAccessSemantics(), *overload); } /// "Finish" an array expression by filling in the semantic expression. @@ -2909,11 +2960,14 @@ namespace { } Expr *visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) { + auto *memberLocator = + cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); return buildSubscript(expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), AccessSemantics::Ordinary); + expr->isImplicit(), AccessSemantics::Ordinary, + solution.getOverloadChoice(memberLocator)); } Expr *visitTupleElementExpr(TupleElementExpr *expr) { @@ -2973,17 +3027,8 @@ namespace { Expr *visitApplyExpr(ApplyExpr *expr) { auto *calleeLoc = CalleeLocators[expr]; assert(calleeLoc); - - // Resolve the callee for the application if we have one. Note that we're - // using `resolveConcreteDeclRef` instead `solution.resolveLocatorToDecl` - // here to benefit from ExprRewriter's cache of concrete refs. - ConcreteDeclRef callee; - if (auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc)) { - auto *decl = overload->choice.getDeclOrNull(); - callee = resolveConcreteDeclRef(decl, calleeLoc); - } - return finishApply(expr, callee, cs.getType(expr), - cs.getConstraintLocator(expr)); + return finishApply(expr, cs.getType(expr), cs.getConstraintLocator(expr), + calleeLoc); } Expr *visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) { @@ -3482,12 +3527,9 @@ namespace { // Convert the subexpression. Expr *sub = expr->getSubExpr(); - solution.setExprTypes(sub); - - if (TypeChecker::convertToType(sub, toType, cs.DC)) + sub = solution.coerceToType(sub, toType, cs.getConstraintLocator(sub)); + if (!sub) return nullptr; - - cs.cacheExprTypes(sub); expr->setSubExpr(sub); cs.setType(expr, toType); @@ -3538,9 +3580,6 @@ namespace { return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - if (cs.getType(sub)->isEqual(toType)) { ctx.Diags.diagnose(expr->getLoc(), diag::forced_downcast_noop, toType) .fixItRemove(SourceRange( @@ -3554,14 +3593,9 @@ namespace { "as"); } - // Transmute the checked cast into a coercion expression. - auto *result = - new (ctx) CoerceExpr(sub, expr->getLoc(), expr->getCastTypeLoc()); - cs.setType(result, toType); - cs.setType(result->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - return visitCoerceExpr(result, disjunctionChoice); + expr->setCastKind(castKind); + cs.setType(expr, toType); + return expr; } // Valid casts. @@ -3614,37 +3648,18 @@ namespace { fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, expr->getCastTypeLoc().getSourceRange()); switch (castKind) { - /// Invalid cast. + // Invalid cast. case CheckedCastKind::Unresolved: expr->setCastKind(CheckedCastKind::ValueCast); break; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - ctx.Diags.diagnose(expr->getLoc(), diag::conditional_downcast_coercion, cs.getType(sub), toType); - - // Transmute the checked cast into a coercion expression. - auto *coerce = - new (ctx) CoerceExpr(sub, expr->getLoc(), expr->getCastTypeLoc()); - cs.setType(coerce, toType); - cs.setType(coerce->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - Expr *result = visitCoerceExpr(coerce, disjunctionChoice); - if (!result) - return nullptr; - - // Wrap the result in an optional. Mark the optional injection as - // explicit, because the user did in fact write the '?' as part of - // 'as?', even though it wasn't necessary. - result = - new (ctx) InjectIntoOptionalExpr(result, OptionalType::get(toType)); - result->setImplicit(false); - return cs.cacheType(result); + expr->setCastKind(castKind); + cs.setType(expr, OptionalType::get(toType)); + return expr; } // Valid casts. @@ -4165,8 +4180,6 @@ namespace { } auto kind = origComponent.getKind(); - Optional foundDecl; - auto locator = cs.getConstraintLocator( E, LocatorPathElt::KeyPathComponent(i)); @@ -4179,48 +4192,42 @@ namespace { // If this is an unresolved link, make sure we resolved it. if (kind == KeyPathExpr::Component::Kind::UnresolvedProperty || kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) { - foundDecl = solution.getOverloadChoiceIfAvailable(locator); - // Leave the component unresolved if the overload was not resolved. - if (foundDecl) { - isDynamicMember = - foundDecl->choice.getKind() == - OverloadChoiceKind::DynamicMemberLookup || - foundDecl->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup; - - // If this was a @dynamicMemberLookup property, then we actually - // form a subscript reference, so switch the kind. - if (isDynamicMember) { - kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; - } + auto foundDecl = solution.getOverloadChoiceIfAvailable(locator); + if (!foundDecl) { + // If we couldn't resolve the component, leave it alone. + resolvedComponents.push_back(origComponent); + baseTy = origComponent.getComponentType(); + continue; + } + + isDynamicMember = + foundDecl->choice.getKind() == + OverloadChoiceKind::DynamicMemberLookup || + foundDecl->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup; + + // If this was a @dynamicMemberLookup property, then we actually + // form a subscript reference, so switch the kind. + if (isDynamicMember) { + kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; } } switch (kind) { case KeyPathExpr::Component::Kind::UnresolvedProperty: { - // If we couldn't resolve the component, leave it alone. - if (!foundDecl) { - resolvedComponents.push_back(origComponent); - break; - } - - buildKeyPathPropertyComponent(*foundDecl, origComponent.getLoc(), + buildKeyPathPropertyComponent(solution.getOverloadChoice(locator), + origComponent.getLoc(), locator, resolvedComponents); break; } case KeyPathExpr::Component::Kind::UnresolvedSubscript: { - // Leave the component unresolved if the overload was not resolved. - if (!foundDecl) { - resolvedComponents.push_back(origComponent); - break; - } - ArrayRef subscriptLabels; if (!isDynamicMember) subscriptLabels = origComponent.getSubscriptLabels(); buildKeyPathSubscriptComponent( - *foundDecl, origComponent.getLoc(), origComponent.getIndexExpr(), + solution.getOverloadChoice(locator), + origComponent.getLoc(), origComponent.getIndexExpr(), subscriptLabels, locator, resolvedComponents); break; } @@ -4457,7 +4464,8 @@ namespace { } void buildKeyPathSubscriptComponent( - SelectedOverload &overload, SourceLoc componentLoc, Expr *indexExpr, + const SelectedOverload &overload, + SourceLoc componentLoc, Expr *indexExpr, ArrayRef labels, ConstraintLocator *locator, SmallVectorImpl &components) { auto subscript = cast(overload.choice.getDecl()); @@ -6448,13 +6456,15 @@ static bool isValidDynamicCallableMethod(FuncDecl *method, // Build a reference to a `callAsFunction` method. static Expr *buildCallAsFunctionMethodRef( ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected, - ConstraintLocatorBuilder applyFunctionLoc) { - auto *fn = apply->getFn(); + ConstraintLocator *calleeLoc) { + assert(calleeLoc->isLastElement()); + assert(cast(selected.choice.getDecl())->isCallAsFunctionMethod()); + // Create direct reference to `callAsFunction` method. + auto *fn = apply->getFn(); auto *declRef = rewriter.buildMemberRef( fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(fn->getEndLoc()), - applyFunctionLoc, applyFunctionLoc, /*implicit*/ true, - AccessSemantics::Ordinary); + calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary); if (!declRef) return nullptr; declRef->setImplicit(apply->isImplicit()); @@ -6542,9 +6552,9 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, return result; } -Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, - Type openedType, - ConstraintLocatorBuilder locator) { +Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator) { auto &ctx = cs.getASTContext(); auto fn = apply->getFn(); @@ -6688,7 +6698,24 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; - // Resolve `callAsFunction` and `@dynamicCallable` applications. + // Resolve the callee for the application if we have one. + ConcreteDeclRef callee; + auto *calleeLoc = cs.getConstraintLocator(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc); + if (overload) { + auto *decl = overload->choice.getDeclOrNull(); + callee = resolveConcreteDeclRef(decl, calleeLoc); + } + + // If this is an implicit call to a `callAsFunction` method, build the + // appropriate member reference. + if (cs.getType(fn)->getRValueType()->isCallableNominalType(dc)) { + fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc); + if (!fn) + return nullptr; + } + + // Resolve a `@dynamicCallable` application. auto applyFunctionLoc = locator.withPathElement(ConstraintLocator::ApplyFunction); if (auto selected = solution.getOverloadChoiceIfAvailable( @@ -6701,15 +6728,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, if (isValidDynamicCallableMethod(method, methodType)) return finishApplyDynamicCallable( apply, *selected, method, methodType, applyFunctionLoc); - - // If this is an implicit call to a callAsFunction method, build the - // appropriate member reference. - if (method->isCallAsFunctionMethod()) { - fn = buildCallAsFunctionMethodRef(*this, apply, *selected, - applyFunctionLoc); - if (!fn) - return nullptr; - } } } @@ -6837,12 +6855,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, assert(ty->getNominalOrBoundGenericNominal() || ty->is() || ty->isExistentialType() || ty->is()); - // We have the constructor. - auto choice = selected->choice; - // Consider the constructor decl reference expr 'implicit', but the // constructor call expr itself has the apply's 'implicitness'. - auto ctorRef = resolveConcreteDeclRef(choice.getDecl(), ctorLocator); Expr *declRef = buildMemberRef(fn, /*dotLoc=*/SourceLoc(), *selected, DeclNameLoc(fn->getEndLoc()), locator, ctorLocator, /*Implicit=*/true, @@ -6853,7 +6867,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, ConcreteDeclRef callee, apply->setFn(declRef); // Tail-recur to actually call the constructor. - return finishApply(apply, ctorRef, openedType, locator); + return finishApply(apply, openedType, locator, ctorLocator); } // Return the precedence-yielding parent of 'expr', along with the index of @@ -7027,9 +7041,8 @@ namespace { // If this closure had a function builder applied, rewrite it to a // closure with a single expression body containing the builder // invocations. - auto builder = - Rewriter.solution.builderTransformedClosures.find(closure); - if (builder != Rewriter.solution.builderTransformedClosures.end()) { + auto builder = Rewriter.solution.functionBuilderTransformed.find(closure); + if (builder != Rewriter.solution.functionBuilderTransformed.end()) { auto singleExpr = builder->second.singleExpr; auto returnStmt = new (ctx) ReturnStmt( singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); @@ -7192,10 +7205,9 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool skipClosures) { +llvm::PointerUnion ConstraintSystem::applySolutionImpl( + Solution &solution, SolutionApplicationTarget target, Type convertType, + bool discardedExpr, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -7214,7 +7226,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. - diagnoseFailureForExpr(expr); + diagnoseFailureFor(target); return nullptr; } } @@ -7222,14 +7234,43 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics()); ExprWalker walker(rewriter); - // Apply the solution to the expression. - auto result = expr->walk(walker); - if (!result) - return nullptr; + // Apply the solution to the target. + llvm::PointerUnion result; + if (auto expr = target.getAsExpr()) { + result = expr->walk(walker); + } else { + auto fn = *target.getAsFunction(); + + // Dig out the function builder transformation we applied. + auto transformed = solution.functionBuilderTransformed.find(fn); + assert(transformed != solution.functionBuilderTransformed.end()); + + auto singleExpr = transformed->second.singleExpr; + singleExpr = singleExpr->walk(walker); + if (!singleExpr) + return result; + + singleExpr = rewriter.coerceToType(singleExpr, + transformed->second.bodyResultType, + getConstraintLocator(singleExpr)); + if (!singleExpr) + return result; + + ASTContext &ctx = getASTContext(); + auto returnStmt = new (ctx) ReturnStmt( + singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); + auto braceStmt = BraceStmt::create( + ctx, returnStmt->getStartLoc(), ASTNode(returnStmt), + returnStmt->getEndLoc(), /*implicit=*/false); + result = braceStmt; + } + + if (result.isNull()) + return result; // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. - if (!skipClosures) { + if (!performingDiagnostics) { bool hadError = false; for (auto *closure : walker.getClosuresToTypeCheck()) hadError |= TypeChecker::typeCheckClosureBody(closure); @@ -7247,28 +7288,36 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, return nullptr; } - // We are supposed to use contextual type only if it is present and - // this expression doesn't represent the implicit return of the single - // expression function which got deduced to be `Never`. - auto shouldCoerceToContextualType = [&]() { - return convertType && !(getType(result)->isUninhabited() && - getContextualTypePurpose() == CTP_ReturnSingleExpr); - }; + if (auto resultExpr = result.dyn_cast()) { + Expr *expr = target.getAsExpr(); + assert(expr && "Can't have expression result without expression target"); + // We are supposed to use contextual type only if it is present and + // this expression doesn't represent the implicit return of the single + // expression function which got deduced to be `Never`. + auto shouldCoerceToContextualType = [&]() { + return convertType && + !(getType(resultExpr)->isUninhabited() && + getContextualTypePurpose() == CTP_ReturnSingleExpr); + }; - // If we're supposed to convert the expression to some particular type, - // do so now. - if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(result, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; - } else if (getType(result)->hasLValueType() && !discardedExpr) { - // We referenced an lvalue. Load it. - result = rewriter.coerceToType(result, getType(result)->getRValueType(), - getConstraintLocator(expr)); + // If we're supposed to convert the expression to some particular type, + // do so now. + if (shouldCoerceToContextualType()) { + result = rewriter.coerceToType(resultExpr, convertType, + getConstraintLocator(expr)); + if (!result) + return nullptr; + } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { + // We referenced an lvalue. Load it. + result = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); + } + + if (resultExpr) + solution.setExprTypes(resultExpr); } - solution.setExprTypes(result); rewriter.finalize(); return result; @@ -7332,6 +7381,37 @@ class SetExprTypes : public ASTWalker { }; } +ProtocolConformanceRef Solution::resolveConformance( + ConstraintLocator *locator, ProtocolDecl *proto) { + for (const auto &conformance : Conformances) { + if (conformance.first != locator) + continue; + if (conformance.second.getRequirement() != proto) + continue; + + // If the conformance doesn't require substitution, return it immediately. + auto conformanceRef = conformance.second; + if (conformanceRef.isAbstract()) + return conformanceRef; + + auto concrete = conformanceRef.getConcrete(); + auto conformingType = concrete->getType(); + if (!conformingType->hasTypeVariable()) + return conformanceRef; + + // Substitute into the conformance type, then look for a conformance + // again. + // FIXME: Should be able to perform the substitution using the Solution + // itself rather than another conforms-to-protocol check. + Type substConformingType = simplifyType(conformingType); + return TypeChecker::conformsToProtocol( + substConformingType, proto, constraintSystem->DC, + ConformanceCheckFlags::InExpression); + } + + return ProtocolConformanceRef::forInvalid(); +} + void Solution::setExprTypes(Expr *expr) const { if (!expr) return; @@ -7386,3 +7466,14 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { assert(getKind() == Ambiguous); return makeArrayRef(solutions, numSolutions); } + +llvm::PointerUnion SolutionApplicationTarget::walk( + ASTWalker &walker) { + switch (kind) { + case Kind::expression: + return getAsExpr()->walk(walker); + + case Kind::function: + return getAsFunction()->getBody()->walk(walker); + } +} diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a129fc838c681..b7b5cbf4b0934 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -610,6 +610,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: // If our type variable shows up in the base type, there's // nothing to do. // FIXME: Can we avoid simplification here? @@ -968,7 +969,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { if (Binding.hasDefaultedLiteralProtocol()) { type = cs.openUnboundGenericType(type, dstLocator); type = type->reconstituteSugar(/*recursive=*/false); - } else if (srcLocator->isLastElement() && + } else if (srcLocator && + srcLocator->isLastElement() && !type->hasTypeVariable() && cs.isCollectionType(type)) { // If the type binding comes from the argument conversion, let's // instead of binding collection types directly, try to bind @@ -999,6 +1001,11 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); if (cs.recordFix(fix)) return true; + } else if (TypeVar->getImpl().isClosureResultType()) { + auto *fix = SpecifyClosureReturnType::create( + cs, TypeVar->getImpl().getLocator()); + if (cs.recordFix(fix)) + return true; } } } diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 7657ec04cd3e2..073e6994254ed 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -201,12 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - /// Attempt to produce a diagnostic for a mismatch between a call's - /// type and its assumed contextual type. - bool diagnoseCallContextualConversionErrors(ApplyExpr *callEpxr, - Type contextualType, - ContextualTypePurpose CTP); - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, ArrayRef argLabels); @@ -226,11 +220,6 @@ class FailureDiagnosis :public ASTVisitor{ std::pair validateContextualType(Type contextualType, ContextualTypePurpose CTP); - /// Check the specified closure to see if it is a multi-statement closure with - /// an uninferred type. If so, diagnose the problem with an error and return - /// true. - bool diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure); - /// Given a result of name lookup that had no viable results, diagnose the /// unviable ones. void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults, @@ -245,22 +234,16 @@ class FailureDiagnosis :public ASTVisitor{ Optional)>> callback = None, bool includeInaccessibleMembers = true); - bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet); - bool visitExpr(Expr *E); bool visitIdentityExpr(IdentityExpr *E); bool visitTryExpr(TryExpr *E); - bool visitUnresolvedMemberExpr(UnresolvedMemberExpr *E); bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE); bool visitArrayExpr(ArrayExpr *E); bool visitDictionaryExpr(DictionaryExpr *E); bool visitObjectLiteralExpr(ObjectLiteralExpr *E); - bool visitSubscriptExpr(SubscriptExpr *SE); bool visitApplyExpr(ApplyExpr *AE); - bool visitCoerceExpr(CoerceExpr *CE); - bool visitIfExpr(IfExpr *IE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); }; } // end anonymous namespace @@ -389,7 +372,6 @@ namespace { llvm::DenseMap ExprTypes; llvm::DenseMap TypeLocTypes; llvm::DenseMap PatternTypes; - llvm::DenseMap ParamDeclInterfaceTypes; ExprTypeSaverAndEraser(const ExprTypeSaverAndEraser&) = delete; void operator=(const ExprTypeSaverAndEraser&) = delete; public: @@ -428,17 +410,6 @@ namespace { if (isa(expr) && !isa(expr) && !(expr->getType() && expr->getType()->hasError())) return { false, expr }; - - // If a ClosureExpr's parameter list has types on the decls, then - // remove them so that they'll get regenerated from the - // associated TypeLocs or resynthesized as fresh typevars. - if (auto *CE = dyn_cast(expr)) - for (auto P : *CE->getParameters()) { - if (P->hasInterfaceType()) { - TS->ParamDeclInterfaceTypes[P] = P->getInterfaceType(); - P->setInterfaceType(Type()); - } - } expr->setType(nullptr); @@ -481,12 +452,6 @@ namespace { for (auto patternElt : PatternTypes) patternElt.first->setType(patternElt.second); - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) { - assert(!paramDeclIfaceElt.first->isImmutable() || - !paramDeclIfaceElt.second->is()); - paramDeclIfaceElt.first->setInterfaceType(paramDeclIfaceElt.second->getInOutObjectType()); - } // Done, don't do redundant work on destruction. ExprTypes.clear(); @@ -513,33 +478,6 @@ namespace { for (auto patternElt : PatternTypes) if (!patternElt.first->hasType()) patternElt.first->setType(patternElt.second); - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) - if (!paramDeclIfaceElt.first->hasInterfaceType()) { - paramDeclIfaceElt.first->setInterfaceType( - getParamBaseType(paramDeclIfaceElt)); - } - } - - private: - static Type getParamBaseType(std::pair &storedParam) { - ParamDecl *param; - Type storedType; - - std::tie(param, storedType) = storedParam; - - // FIXME: We are currently in process of removing `InOutType` - // so `VarDecl::get{Interface}Type` is going to wrap base - // type into `InOutType` if its flag indicates that it's - // an `inout` parameter declaration. But such type can't - // be restored directly using `VarDecl::set{Interface}Type` - // caller needs additional logic to extract base type. - if (auto *IOT = storedType->getAs()) { - assert(param->isInOut()); - return IOT->getObjectType(); - } - - return storedType; } }; } // end anonymous namespace @@ -606,10 +544,6 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // to diagnose a problem. TCEOptions |= TypeCheckExprFlags::SubExpressionDiagnostics; - // Don't walk into non-single expression closure bodies, because - // ExprTypeSaver and TypeNullifier skip them too. - TCEOptions |= TypeCheckExprFlags::SkipMultiStmtClosures; - // Claim that the result is discarded to preserve the lvalue type of // the expression. if (options.contains(TCC_AllowLValue)) @@ -1473,234 +1407,6 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, return false; } -bool FailureDiagnosis::diagnoseSubscriptErrors(SubscriptExpr *SE, - bool inAssignmentDestination) { - auto baseExpr = typeCheckChildIndependently(SE->getBase()); - if (!baseExpr) return true; - auto baseType = CS.getType(baseExpr); - - if (isa(baseExpr)) { - diagnose(baseExpr->getLoc(), diag::cannot_subscript_nil_literal) - .highlight(baseExpr->getSourceRange()); - return true; - } - - std::function)> callback = - [&](ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(Type(), candidates, SE->hasTrailingClosure(), - CS, /*selfAlreadyApplied*/ false); - - // We're about to typecheck the index list, which needs to be processed with - // self already applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; - - auto indexExpr = - typeCheckArgumentChildIndependently(SE->getIndex(), Type(), calleeInfo); - if (!indexExpr) - return true; - - // Back to analyzing the candidate list with self applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = false; - - ArrayRef argLabels = SE->getArgumentLabels(); - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; - - auto indexType = CS.getType(indexExpr); - - auto decomposedBaseType = decomposeArgType(baseType, {Identifier()}); - auto decomposedIndexType = decomposeArgType(indexType, argLabels); - calleeInfo.filterList( - [&](OverloadCandidate cand) -> CalleeCandidateInfo::ClosenessResultTy { - // Classify how close this match is. Non-subscript decls don't match. - auto subscriptDecl = dyn_cast_or_null(cand.getDecl()); - if (!subscriptDecl || - (inAssignmentDestination && !subscriptDecl->supportsMutation())) - return {CC_GeneralMismatch, {}}; - - // Check whether the self type matches. - auto selfConstraint = CC_ExactMatch; - if (calleeInfo.evaluateCloseness(cand, decomposedBaseType).first != - CC_ExactMatch) - selfConstraint = CC_SelfMismatch; - - // Set a flag to look past the self argument to the indices. - cand.skipCurriedSelf = true; - - // Explode out multi-index subscripts to find the best match. - auto indexResult = - calleeInfo.evaluateCloseness(cand, decomposedIndexType); - if (selfConstraint > indexResult.first) - return {selfConstraint, {}}; - return indexResult; - }); - - // If the closest matches all mismatch on self, we either have something - // that cannot be subscripted, or an ambiguity. - if (calleeInfo.closeness == CC_SelfMismatch) { - diagnose(SE->getLoc(), diag::cannot_subscript_base, baseType) - .highlight(SE->getBase()->getSourceRange()); - // FIXME: Should suggest overload set, but we're not ready for that until - // it points to candidates and identifies the self type in the diagnostic. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; - } - - // Any other failures relate to the index list. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; - - // TODO: Is there any reason to check for CC_NonLValueInOut here? - - if (calleeInfo.closeness == CC_ExactMatch) { - auto message = diag::ambiguous_subscript; - - // If there is an exact match on the argument with - // a single candidate, let's type-check subscript - // as a whole to figure out if there is any structural - // problem after all. - if (calleeInfo.size() == 1) { - Expr *expr = SE; - ConcreteDeclRef decl = nullptr; - message = diag::cannot_subscript_with_index; - - if (TypeChecker::getTypeOfExpressionWithoutApplying(expr, CS.DC, decl)) - return false; - - // If we are down to a single candidate but with an unresolved - // index type, we can substitute in the base type to get a simpler - // and more concrete expected type for this subscript decl, in order - // to diagnose a better error. - if (baseType && indexType->hasUnresolvedType()) { - auto cand = calleeInfo.candidates[0]; - auto candType = baseType->getTypeOfMember(CS.DC->getParentModule(), - cand.getDecl(), nullptr); - if (auto *candFunc = candType->getAs()) { - auto paramsType = FunctionType::composeInput(CS.getASTContext(), - candFunc->getParams(), - false); - if (!typeCheckChildIndependently( - indexExpr, paramsType, CTP_CallArgument, TCC_ForceRecheck)) - return true; - } - } - } - - diagnose(SE->getLoc(), message, baseType, indexType) - .highlight(indexExpr->getSourceRange()) - .highlight(baseExpr->getSourceRange()); - - // FIXME: suggestPotentialOverloads should do this. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - for (auto candidate : calleeInfo.candidates) - if (auto decl = candidate.getDecl()) - diagnose(decl, diag::found_candidate); - else - diagnose(candidate.getExpr()->getLoc(), diag::found_candidate); - - return true; - } - - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; - - // Diagnose some simple and common errors. - if (calleeInfo.diagnoseSimpleErrors(SE)) - return true; - - diagnose(SE->getLoc(), diag::cannot_subscript_with_index, baseType, - indexType); - - calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; - }; - - auto locator = - CS.getConstraintLocator(SE, ConstraintLocator::SubscriptMember); - - return diagnoseMemberFailures(SE, baseExpr, ConstraintKind::ValueMember, - DeclNameRef::createSubscript(), - FunctionRefKind::DoubleApply, locator, - callback); -} - -bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) { - return diagnoseSubscriptErrors(SE, /* inAssignmentDestination = */ false); -} - -namespace { - /// Type checking listener for pattern binding initializers. - class CalleeListener : public ExprTypeCheckListener { - Type contextualType; - public: - explicit CalleeListener(Type contextualType) - : contextualType(contextualType) { } - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // If we have no contextual type, there is nothing to do. - if (!contextualType) - return false; - - // If the expression is obviously something that produces a metatype, - // then don't put a constraint on it. - auto semExpr = expr->getValueProvidingExpr(); - if (isa(semExpr)) - return false; - - auto resultLocator = - cs.getConstraintLocator(expr, ConstraintLocator::FunctionResult); - auto resultType = cs.createTypeVariable(resultLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - auto locator = cs.getConstraintLocator(expr); - cs.addConstraint(ConstraintKind::FunctionResult, - cs.getType(expr), - resultType, - locator); - - cs.addConstraint(ConstraintKind::Conversion, - resultType, - contextualType, - locator); - - return false; - } - }; -} // end anonymous namespace - -/// Check if there failure associated with expression is related -/// to given contextual type. -bool FailureDiagnosis::diagnoseCallContextualConversionErrors( - ApplyExpr *callExpr, Type contextualType, ContextualTypePurpose CTP) { - if (!contextualType || contextualType->hasUnresolvedType()) - return false; - - auto typeCheckExpr = [&](Expr *expr, DeclContext *DC, - SmallPtrSetImpl &types) { - getPossibleTypesOfExpressionWithoutApplying( - expr, DC, types, FreeTypeVariableBinding::Disallow); - }; - - // First let's type-check expression without contextual type, and - // see if that's going to produce a type, if so, let's type-check - // again, this time using given contextual type. - SmallPtrSet withoutContextual; - typeCheckExpr(callExpr, CS.DC, withoutContextual); - - // If there are no types returned, it means that problem was - // nothing to do with contextual information, probably parameter/argument - // mismatch. - if (withoutContextual.empty()) - return false; - - Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type(); - return diagnoseContextualConversionError(callExpr, contextualType, CTP, - exprType); -} - // Check if there is a structural problem in the function expression // by performing type checking with the option to allow unresolved // type variables. If that is going to produce a function type with @@ -1731,45 +1437,8 @@ static bool shouldTypeCheckFunctionExpr(FailureDiagnosis &FD, DeclContext *DC, return true; } -// Check if any candidate of the overload set can accept a specified -// number of arguments, regardless of parameter type or label information. -static bool isViableOverloadSet(const CalleeCandidateInfo &CCI, - size_t numArgs) { - for (unsigned i = 0; i < CCI.size(); ++i) { - auto &&cand = CCI[i]; - auto funcDecl = dyn_cast_or_null(cand.getDecl()); - - // If we don't have a func decl or we haven't resolved its parameters, - // continue. The latter case can occur with `type(of:)`, which is introduced - // as a type variable. - if (!funcDecl || !cand.hasParameters()) - continue; - - auto params = cand.getParameters(); - bool hasVariadicParameter = false; - auto pairMatcher = [&](unsigned argIdx, unsigned paramIdx) { - hasVariadicParameter |= params[paramIdx].isVariadic(); - return true; - }; - - auto paramInfo = cand.getParameterListInfo(params); - InputMatcher IM(params, paramInfo); - auto result = IM.match(numArgs, pairMatcher); - if (result == InputMatcher::IM_Succeeded) - return true; - if (result == InputMatcher::IM_HasUnclaimedInput && hasVariadicParameter) - return true; - } - return false; -} - bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { - if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType(), - CS.getContextualTypePurpose())) - return true; - auto *fnExpr = callExpr->getFn(); - auto originalFnType = CS.getType(callExpr->getFn()); if (shouldTypeCheckFunctionExpr(*this, CS.DC, fnExpr)) { // Type check the function subexpression to resolve a type for it if @@ -1792,162 +1461,12 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { auto fnType = getFuncType(CS.getType(fnExpr)); - // Let's see if this has to do with member vs. property error - // because sometimes when there is a member and a property declared - // on the nominal type with the same name. Type-checking function - // expression separately from arguments might produce solution for - // the property instead of the member. - if (!fnType->is() && - isa(callExpr->getFn())) { - fnExpr = callExpr->getFn(); - - SmallPtrSet types; - getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types); - - auto isFunctionType = [getFuncType](Type type) -> bool { - return type && getFuncType(type)->is(); - }; - - auto fnTypes = std::find_if(types.begin(), types.end(), isFunctionType); - if (fnTypes != types.end()) { - auto funcType = getFuncType(*fnTypes); - // If there is only one function type, let's use it. - if (std::none_of(std::next(fnTypes), types.end(), isFunctionType)) - fnType = funcType; - } else { - fnType = getFuncType(originalFnType); - } - } - - // If we have a contextual type, and if we have an ambiguously typed function - // result from our previous check, we re-type-check it using this contextual - // type to inform the result type of the callee. - // - // We only do this as a second pass because the first pass we just did may - // return something of obviously non-function-type. If this happens, we - // produce better diagnostics below by diagnosing this here rather than trying - // to peel apart the failed conversion to function type. - if (CS.getContextualType() && - (isUnresolvedOrTypeVarType(fnType) || - (fnType->is() && fnType->hasUnresolvedType()))) { - // FIXME: Prevent typeCheckChildIndependently from transforming expressions, - // because if we try to typecheck OSR expression with contextual type, - // it'll end up converting it into DeclRefExpr based on contextual info, - // instead let's try to get a type without applying and filter callee - // candidates later on. - CalleeListener listener(CS.getContextualType()); - - if (isa(fnExpr)) { - assert(!cast(fnExpr)->getReferencedDecl() && - "unexpected declaration reference"); - - ConcreteDeclRef decl = nullptr; - Type type = TypeChecker::getTypeOfExpressionWithoutApplying( - fnExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType, - &listener); - - if (type) - fnType = getFuncType(type); - } else { - fnExpr = typeCheckChildIndependently(callExpr->getFn(), Type(), - CTP_CalleeResult, TCC_ForceRecheck, - &listener); - if (!fnExpr) - return true; - - fnType = getFuncType(CS.getType(fnExpr)); - } - } - - // If we resolved a concrete expression for the callee, and it has - // non-function/non-metatype type, then we cannot call it! - if (!isUnresolvedOrTypeVarType(fnType) && - !fnType->is() && !fnType->is()) { - auto arg = callExpr->getArg(); - - // If the argument is a trailing ClosureExpr (i.e. {....}) and it is on - // the line after the callee, then it's likely the user forgot to - // write "do" before their brace stmt. - // Note that line differences of more than 1 are diagnosed during parsing. - if (auto *PE = dyn_cast(arg)) { - if (PE->hasTrailingClosure() && isa(PE->getSubExpr())) { - auto *closure = cast(PE->getSubExpr()); - auto &SM = CS.getASTContext().SourceMgr; - if (closure->hasAnonymousClosureVars() && - closure->getParameters()->size() == 0 && - 1 + SM.getLineNumber(callExpr->getFn()->getEndLoc()) == - SM.getLineNumber(closure->getStartLoc())) { - diagnose(closure->getStartLoc(), diag::brace_stmt_suggest_do) - .fixItInsert(closure->getStartLoc(), "do "); - return true; - } - } - } - - auto isExistentialMetatypeType = fnType->is(); - if (isExistentialMetatypeType) { - auto diag = diagnose(arg->getStartLoc(), - diag::missing_init_on_metatype_initialization); - diag.highlight(fnExpr->getSourceRange()); - return true; - } else { - auto diag = diagnose(arg->getStartLoc(), - diag::cannot_call_non_function_value, fnType); - diag.highlight(fnExpr->getSourceRange()); - - // If the argument is an empty tuple, then offer a - // fix-it to remove the empty tuple and use the value - // directly. - if (auto tuple = dyn_cast(arg)) { - if (tuple->getNumElements() == 0) { - diag.fixItRemove(arg->getSourceRange()); - } - } - return true; - } - } - bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); // Collect a full candidate list of callees based on the partially type // checked function. CalleeCandidateInfo calleeInfo(fnExpr, hasTrailingClosure, CS); - // In the case that function subexpression was resolved independently in - // the first place, the resolved type may not provide the best diagnostic. - // We consider the number of arguments to decide whether we'd go with it or - // stay with the original one. - if (fnExpr != callExpr->getFn()) { - bool isInstanceMethodAsCurriedMemberOnType = false; - if (!calleeInfo.empty()) { - auto &&cand = calleeInfo[0]; - auto decl = cand.getDecl(); - if (decl && decl->isInstanceMember() && !cand.skipCurriedSelf && - cand.getParameters().size() == 1) - isInstanceMethodAsCurriedMemberOnType = true; - } - - // In terms of instance method as curried member on type, we should not - // take the number of arguments into account. - if (!isInstanceMethodAsCurriedMemberOnType) { - size_t numArgs = 1; - auto arg = callExpr->getArg(); - if (auto tuple = dyn_cast(arg)) { - numArgs = tuple->getNumElements(); - } - - if (!isViableOverloadSet(calleeInfo, numArgs)) { - CalleeCandidateInfo calleeInfoOrig(callExpr->getFn(), - hasTrailingClosure, CS); - if (isViableOverloadSet(calleeInfoOrig, numArgs)) { - fnExpr = callExpr->getFn(); - fnType = getFuncType(CS.getType(fnExpr)); - calleeInfo = calleeInfoOrig; - } - } - } - } - // Filter list of the candidates based on the known function type. if (auto fn = fnType->getAs()) { using Closeness = CalleeCandidateInfo::ClosenessResultTy; @@ -2033,21 +1552,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { calleeInfo, TCC_ForceRecheck); if (!argExpr) return true; // already diagnosed. - - // Handle argument label mismatches when we have multiple candidates. - if (calleeInfo.closeness == CC_ArgumentLabelMismatch) { - auto args = decomposeArgType(CS.getType(argExpr), argLabels); - - // If we have multiple candidates that we fail to match, just say we have - // the wrong labels and list the candidates out. - diagnose(callExpr->getLoc(), diag::wrong_argument_labels_overload, - getParamListAsString(args)) - .highlight(argExpr->getSourceRange()); - - // Did the user intend on invoking a different overload? - calleeInfo.suggestPotentialOverloads(fnExpr->getLoc()); - return true; - } auto overloadName = calleeInfo.declName; @@ -2213,58 +1717,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { return true; } -bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { - // Coerce the input to whatever type is specified by the CoerceExpr. - auto expr = typeCheckChildIndependently(CE->getSubExpr(), - CS.getType(CE->getCastTypeLoc()), - CTP_CoerceOperand); - if (!expr) - return true; - - auto ref = expr->getReferencedDecl(); - if (auto *decl = ref.getDecl()) { - // Without explicit coercion we might end up - // type-checking sub-expression as unavaible - // declaration, let's try to diagnose that here. - if (AvailableAttr::isUnavailable(decl)) - return diagnoseExplicitUnavailability( - decl, expr->getSourceRange(), CS.DC, dyn_cast(expr)); - } - - return false; -} - -bool FailureDiagnosis::visitIfExpr(IfExpr *IE) { - auto typeCheckClauseExpr = [&](Expr *clause, Type contextType = Type(), - ContextualTypePurpose convertPurpose = - CTP_Unused) -> Expr * { - // Provide proper contextual type when type conversion is specified. - return typeCheckChildIndependently(clause, contextType, convertPurpose, - TCCOptions(), nullptr, false); - }; - // Check all of the subexpressions independently. - auto condExpr = typeCheckClauseExpr(IE->getCondExpr()); - if (!condExpr) return true; - auto trueExpr = typeCheckClauseExpr(IE->getThenExpr(), CS.getContextualType(), - CS.getContextualTypePurpose()); - if (!trueExpr) return true; - auto falseExpr = typeCheckClauseExpr( - IE->getElseExpr(), CS.getContextualType(), CS.getContextualTypePurpose()); - if (!falseExpr) return true; - - // If the true/false values already match, it must be a contextual problem. - if (CS.getType(trueExpr)->isEqual(CS.getType(falseExpr))) - return false; - - // Otherwise, the true/false result types must not be matching. - diagnose(IE->getColonLoc(), diag::if_expr_cases_mismatch, - CS.getType(trueExpr), CS.getType(falseExpr)) - .highlight(trueExpr->getSourceRange()) - .highlight(falseExpr->getSourceRange()); - return true; -} - - bool FailureDiagnosis:: visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { // Don't walk the children for this node, it leads to multiple diagnostics @@ -2459,65 +1911,6 @@ bool FailureDiagnosis::visitObjectLiteralExpr(ObjectLiteralExpr *E) { return true; } -bool FailureDiagnosis::visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { - // If we have no contextual type, there is no way to resolve this. Just - // diagnose this as an ambiguity. - if (!CS.getContextualType()) - return false; - - // OTOH, if we do have a contextual type, we can provide a more specific - // error. Dig out the UnresolvedValueMember constraint for this expr node. - Constraint *memberConstraint = nullptr; - auto checkConstraint = [&](Constraint *C) { - if (C->getKind() == ConstraintKind::UnresolvedValueMember && - simplifyLocatorToAnchor(C->getLocator()) == E) - memberConstraint = C; - }; - - if (CS.failedConstraint) - checkConstraint(CS.failedConstraint); - for (auto &C : CS.getConstraints()) { - if (memberConstraint) break; - checkConstraint(&C); - } - - // If we can't find the member constraint in question, then we failed. - if (!memberConstraint) - return false; - - std::function)> callback = [&]( - ArrayRef candidates) { - bool hasTrailingClosure = callArgHasTrailingClosure(E->getArgument()); - - // Dump all of our viable candidates into a CalleeCandidateInfo & sort it - // out. - CalleeCandidateInfo candidateInfo(Type(), candidates, hasTrailingClosure, - CS); - - // Filter the candidate list based on the argument we may or may not have. - candidateInfo.filterContextualMemberList(E->getArgument()); - - // If we have multiple candidates, then we have an ambiguity. - if (candidateInfo.size() != 1) { - SourceRange argRange; - if (auto arg = E->getArgument()) - argRange = arg->getSourceRange(); - diagnose(E->getNameLoc(), diag::ambiguous_member_overload_set, - E->getName()) - .highlight(argRange); - candidateInfo.suggestPotentialOverloads(E->getNameLoc().getBaseNameLoc()); - return true; - } - - return false; - }; - - return diagnoseMemberFailures(E, nullptr, memberConstraint->getKind(), - memberConstraint->getMember(), - memberConstraint->getFunctionRefKind(), - memberConstraint->getLocator(), callback); -} - bool FailureDiagnosis::diagnoseMemberFailures( Expr *E, Expr *baseExpr, ConstraintKind lookupKind, DeclNameRef memberName, FunctionRefKind funcRefKind, ConstraintLocator *locator, @@ -2823,30 +2216,36 @@ bool FailureDiagnosis::diagnoseExprFailure() { /// /// This is guaranteed to always emit an error message. /// -void ConstraintSystem::diagnoseFailureForExpr(Expr *expr) { +void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { setPhase(ConstraintSystemPhase::Diagnostics); SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); }; - // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. - if (auto *RB = dyn_cast(expr)) - expr = RB->getSubExpr(); + if (auto expr = target.getAsExpr()) { + // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. + if (auto *RB = dyn_cast(expr)) + expr = RB->getSubExpr(); - FailureDiagnosis diagnosis(expr, *this); + FailureDiagnosis diagnosis(expr, *this); - // Now, attempt to diagnose the failure from the info we've collected. - if (diagnosis.diagnoseExprFailure()) - return; + // Now, attempt to diagnose the failure from the info we've collected. + if (diagnosis.diagnoseExprFailure()) + return; - // If this is a contextual conversion problem, dig out some information. - if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(), - getContextualTypePurpose())) - return; + // If this is a contextual conversion problem, dig out some information. + if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(), + getContextualTypePurpose())) + return; - // If no one could find a problem with this expression or constraint system, - // then it must be well-formed... but is ambiguous. Handle this by diagnostic - // various cases that come up. - diagnosis.diagnoseAmbiguity(expr); + // If no one could find a problem with this expression or constraint system, + // then it must be well-formed... but is ambiguous. Handle this by diagnostic + // various cases that come up. + diagnosis.diagnoseAmbiguity(expr); + } else { + // Emit a poor fallback message. + getASTContext().Diags.diagnose( + target.getAsFunction()->getLoc(), diag::failed_to_produce_diagnostic); + } } std::pair @@ -2917,138 +2316,6 @@ FailureDiagnosis::validateContextualType(Type contextualType, return {contextualType, CTP}; } -/// Check the specified closure to see if it is a multi-statement closure with -/// an uninferred type. If so, diagnose the problem with an error and return -/// true. -bool FailureDiagnosis:: -diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure) { - if (closure->hasSingleExpressionBody() || - closure->hasExplicitResultType()) - return false; - - auto closureType = CS.getType(closure)->getAs(); - if (!closureType || - !(closureType->getResult()->hasUnresolvedType() || - closureType->getResult()->hasTypeVariable())) - return false; - - // Okay, we have a multi-statement closure expr that has no inferred result, - // type, in the context of a larger expression. The user probably expected - // the compiler to infer the result type of the closure from the body of the - // closure, which Swift doesn't do for multi-statement closures. Try to be - // helpful by digging into the body of the closure, looking for a return - // statement, and inferring the result type from it. If we can figure that - // out, we can produce a fixit hint. - class ReturnStmtFinder : public ASTWalker { - SmallVectorImpl &returnStmts; - public: - ReturnStmtFinder(SmallVectorImpl &returnStmts) - : returnStmts(returnStmts) {} - - // Walk through statements, so we find returns hiding in if/else blocks etc. - std::pair walkToStmtPre(Stmt *S) override { - // Keep track of any return statements we find. - if (auto RS = dyn_cast(S)) - returnStmts.push_back(RS); - return { true, S }; - } - - // Don't walk into anything else, since they cannot contain statements - // that can return from the current closure. - std::pair walkToExprPre(Expr *E) override { - return { false, E }; - } - std::pair walkToPatternPre(Pattern *P) override { - return { false, P }; - } - bool walkToDeclPre(Decl *D) override { return false; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } - bool walkToTypeReprPre(TypeRepr *T) override { return false; } - bool walkToParameterListPre(ParameterList *PL) override { return false; } - }; - - SmallVector Returns; - closure->getBody()->walk(ReturnStmtFinder(Returns)); - - // If we found a return statement inside of the closure expression, then go - // ahead and type check the body to see if we can determine a type. - for (auto RS : Returns) { - llvm::SaveAndRestore SavedDC(CS.DC, closure); - - // Otherwise, we're ok to type check the subexpr. - Type resultType; - if (RS->hasResult()) { - auto resultExpr = RS->getResult(); - ConcreteDeclRef decl = nullptr; - - // If return expression uses closure parameters, which have/are - // type variables, such means that we won't be able to - // type-check result correctly and, unfortunately, - // we are going to leak type variables from the parent - // constraint system through declaration types. - bool hasUnresolvedParams = false; - resultExpr->forEachChildExpr([&](Expr *childExpr) -> Expr *{ - if (auto DRE = dyn_cast(childExpr)) { - if (auto param = dyn_cast(DRE->getDecl())) { - auto paramType = - param->hasInterfaceType() ? param->getType() : Type(); - if (!paramType || paramType->hasTypeVariable()) { - hasUnresolvedParams = true; - return nullptr; - } - } - } - return childExpr; - }); - - if (hasUnresolvedParams) - continue; - - ConstraintSystem::preCheckExpression(resultExpr, CS.DC, &CS); - - // Obtain type of the result expression without applying solutions, - // because otherwise this might result in leaking of type variables, - // since we are not resetting result statement and if expression is - // successfully type-checked its type cleanup is going to be disabled - // (we are allowing unresolved types), and as a side-effect it might - // also be transformed e.g. OverloadedDeclRefExpr -> DeclRefExpr. - auto type = TypeChecker::getTypeOfExpressionWithoutApplying( - resultExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType); - if (type) - resultType = type->getRValueType(); - } - - // If we found a type, presuppose it was the intended result and insert a - // fixit hint. - if (resultType && !isUnresolvedOrTypeVarType(resultType)) { - // If there is a location for an 'in' token, then the argument list was - // specified somehow but no return type was. Insert a "-> ReturnType " - // before the in token. - if (closure->getInLoc().isValid()) { - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsert(closure->getInLoc(), diag::insert_closure_return_type, - resultType, /*argListSpecified*/ false); - return true; - } - - // Otherwise, the closure must take zero arguments. We know this - // because the if one or more argument is specified, a multi-statement - // closure *must* name them, or explicitly ignore them with "_ in". - // - // As such, we insert " () -> ReturnType in " right after the '{' that - // starts the closure body. - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsertAfter(closure->getBody()->getLBraceLoc(), - diag::insert_closure_return_type, resultType, - /*argListSpecified*/ true); - return true; - } - } - - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type); - return true; -} - /// Emit an ambiguity diagnostic about the specified expression. void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { if (auto *assignment = dyn_cast(E)) { @@ -3078,11 +2345,6 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { // Unresolved/Anonymous ClosureExprs are common enough that we should give // them tailored diagnostics. if (auto CE = dyn_cast(E->getValueProvidingExpr())) { - // If this is a multi-statement closure with no explicit result type, emit - // a note to clue the developer in. - if (diagnoseAmbiguousMultiStatementClosure(CE)) - return; - diagnose(E->getLoc(), diag::cannot_infer_closure_type) .highlight(E->getSourceRange()); return; @@ -3124,22 +2386,6 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { return; } - // A very common cause of this diagnostic is a situation where a closure expr - // has no inferred type, due to being a multiline closure. Check to see if - // this is the case and (if so), speculatively diagnose that as the problem. - bool didDiagnose = false; - E->forEachChildExpr([&](Expr *subExpr) -> Expr*{ - auto closure = dyn_cast(subExpr); - if (!didDiagnose && closure) - didDiagnose = diagnoseAmbiguousMultiStatementClosure(closure); - - return subExpr; - }); - - if (didDiagnose) return; - - - // Attempt to re-type-check the entire expression, allowing ambiguity, but // ignoring a contextual type. if (expr == E) { diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index f5f7d757baf76..e65530e3bc20f 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1853,6 +1853,9 @@ bool ContextualFailure::diagnoseAsError() { getFromType(), getToType()); return true; } + + if (diagnoseCoercionToUnrelatedType()) + return true; return false; } @@ -1902,6 +1905,20 @@ bool ContextualFailure::diagnoseAsError() { diagnostic = diag::cannot_convert_condition_value; break; } + + case ConstraintLocator::InstanceType: { + if (diagnoseCoercionToUnrelatedType()) + return true; + break; + } + + case ConstraintLocator::TernaryBranch: { + auto *ifExpr = cast(getRawAnchor()); + fromType = getType(ifExpr->getThenExpr()); + toType = getType(ifExpr->getElseExpr()); + diagnostic = diag::if_expr_cases_mismatch; + break; + } case ConstraintLocator::ContextualType: { if (diagnoseConversionToBool()) @@ -2220,6 +2237,29 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { return true; } +bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { + auto *anchor = getAnchor(); + + if (auto *coerceExpr = dyn_cast(anchor)) { + auto fromType = getType(coerceExpr->getSubExpr()); + auto toType = getType(coerceExpr->getCastTypeLoc()); + + auto diagnostic = + getDiagnosticFor(CTP_CoerceOperand, + /*forProtocol=*/toType->isExistentialType()); + + auto diag = + emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); + diag.highlight(anchor->getSourceRange()); + + (void)tryFixIts(diag); + + return true; + } + + return false; +} + bool ContextualFailure::diagnoseConversionToBool() const { auto toType = getToType(); if (!toType->isBool()) @@ -2565,6 +2605,10 @@ bool ContextualFailure::trySequenceSubsequenceFixIts( if (getFromType()->isEqual(Substring)) { if (getToType()->isEqual(String)) { auto *anchor = getAnchor()->getSemanticsProvidingExpr(); + if (auto *CE = dyn_cast(anchor)) { + anchor = CE->getSubExpr(); + } + auto range = anchor->getSourceRange(); diagnostic.fixItInsert(range.Start, "String("); diagnostic.fixItInsertAfter(range.End, ")"); @@ -3780,6 +3824,21 @@ bool MissingArgumentsFailure::diagnoseAsError() { return true; } +bool MissingArgumentsFailure::diagnoseAsNote() { + auto *locator = getLocator(); + if (auto overload = getChoiceFor(locator)) { + auto *fn = resolveType(overload->openedType)->getAs(); + auto loc = overload->choice.getDecl()->getLoc(); + if (loc.isInvalid()) + loc = getAnchor()->getLoc(); + emitDiagnostic(loc, diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + return true; + } + + return false; +} + bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { auto &ctx = getASTContext(); @@ -5598,6 +5657,19 @@ bool ExtraneousCallFailure::diagnoseAsError() { } } + if (auto *UDE = dyn_cast(anchor)) { + auto *baseExpr = UDE->getBase(); + auto *call = cast(getRawAnchor()); + + if (getType(baseExpr)->isAnyObject()) { + emitDiagnostic(anchor->getLoc(), diag::cannot_call_with_params, + UDE->getName().getBaseName().userFacingName(), + getType(call->getArg())->getString(), + isa(baseExpr)); + return true; + } + } + auto diagnostic = emitDiagnostic( anchor->getLoc(), diag::cannot_call_non_function_value, getType(anchor)); removeParensFixIt(diagnostic); @@ -5902,3 +5974,31 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { .highlight(anchor->getSourceRange()); return true; } + +bool UnableToInferClosureReturnType::diagnoseAsError() { + auto *closure = cast(getRawAnchor()); + + auto diagnostic = + emitDiagnostic(closure->getLoc(), + diag::cannot_infer_closure_result_type, + closure->hasSingleExpressionBody()); + + // If there is a location for an 'in' token, then the argument list was + // specified somehow but no return type was. Insert a "-> ReturnType " + // before the in token. + if (closure->getInLoc().isValid()) { + diagnostic.fixItInsert(closure->getInLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/false); + } else if (closure->getParameters()->size() == 0) { + // Otherwise, the closure must take zero arguments. + // + // As such, we insert " () -> ReturnType in " right after the '{' that + // starts the closure body. + diagnostic.fixItInsertAfter(closure->getBody()->getLBraceLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/true); + } + + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 54cdc3f2b9d70..5628d42ae2c75 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -554,6 +554,9 @@ class ContextualFailure : public FailureDiagnostic { /// If we're trying to convert something to `nil`. bool diagnoseConversionToNil() const; + + /// Diagnose failed conversion in a `CoerceExpr`. + bool diagnoseCoercionToUnrelatedType() const; // If we're trying to convert something of type "() -> T" to T, // then we probably meant to call the value. @@ -1207,6 +1210,8 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; + bool diagnoseAsNote() override; + bool diagnoseSingleMissingArgument() const; private: @@ -1912,6 +1917,15 @@ class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic { bool diagnoseAsError(); }; +class UnableToInferClosureReturnType final : public FailureDiagnostic { +public: + UnableToInferClosureReturnType(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2788265e4d769..21abf55ddd143 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -472,14 +472,15 @@ UseSubscriptOperator *UseSubscriptOperator::create(ConstraintSystem &cs, bool DefineMemberBasedOnUse::diagnose(bool asNote) const { auto failure = MissingMemberFailure(getConstraintSystem(), BaseType, Name, getLocator()); - return failure.diagnose(asNote); + return AlreadyDiagnosed || failure.diagnose(asNote); } DefineMemberBasedOnUse * DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType, - DeclNameRef member, ConstraintLocator *locator) { + DeclNameRef member, bool alreadyDiagnosed, + ConstraintLocator *locator) { return new (cs.getAllocator()) - DefineMemberBasedOnUse(cs, baseType, member, locator); + DefineMemberBasedOnUse(cs, baseType, member, alreadyDiagnosed, locator); } AllowMemberRefOnExistential * @@ -1155,3 +1156,15 @@ SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create( return new (cs.getAllocator()) SpecifyBaseTypeForContextualMember(cs, member, locator); } + +bool SpecifyClosureReturnType::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + UnableToInferClosureReturnType failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyClosureReturnType * +SpecifyClosureReturnType::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyClosureReturnType(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 3bb7a2e0ac3a8..d8550f6ef09a9 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -231,6 +231,10 @@ enum class FixKind : uint8_t { /// Base type in reference to the contextual member e.g. `.foo` couldn't be /// inferred and has to be specified explicitly. SpecifyBaseTypeForContextualMember, + + /// Closure return type has to be explicitly specified because it can't be + /// inferred in current context e.g. because it's a multi-statement closure. + SpecifyClosureReturnType, }; class ConstraintFix { @@ -802,10 +806,19 @@ class DefineMemberBasedOnUse final : public ConstraintFix { Type BaseType; DeclNameRef Name; + /// Whether or not the member error is already diagnosed. This can happen + /// when referencing an erroneous member, and the error is diagnosed at the + /// member declaration. + /// + /// We still want to define erroneous members based on use in order to find + /// a solution through the new diagnostic infrastructure, but we don't + /// want to report a second error message. + bool AlreadyDiagnosed; + DefineMemberBasedOnUse(ConstraintSystem &cs, Type baseType, DeclNameRef member, - ConstraintLocator *locator) + bool alreadyDiagnosed, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator), - BaseType(baseType), Name(member) {} + BaseType(baseType), Name(member), AlreadyDiagnosed(alreadyDiagnosed) {} public: std::string getName() const override { @@ -818,7 +831,7 @@ class DefineMemberBasedOnUse final : public ConstraintFix { bool diagnose(bool asNote = false) const override; static DefineMemberBasedOnUse *create(ConstraintSystem &cs, Type baseType, - DeclNameRef member, + DeclNameRef member, bool alreadyDiagnosed, ConstraintLocator *locator); }; @@ -1605,6 +1618,21 @@ class SpecifyBaseTypeForContextualMember final : public ConstraintFix { create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); }; +class SpecifyClosureReturnType final : public ConstraintFix { + SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {} + +public: + std::string getName() const { + return "specify closure return type"; + } + + bool diagnose(bool asNote = false) const; + + static SpecifyClosureReturnType *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 05a16678c527e..035428dc0e241 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -930,6 +930,16 @@ namespace { = { nullptr, nullptr }; unsigned currentEditorPlaceholderVariable = 0; + /// Returns false and emits the specified diagnostic if the member reference + /// base is a nil literal. Returns true otherwise. + bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { + if (auto nilLiteral = dyn_cast(base)) { + CS.getASTContext().Diags.diagnose(nilLiteral->getLoc(), diagnostic); + return false; + } + return true; + } + /// Add constraints for a reference to a named member of the given /// base type, and return the type of such a reference. Type addMemberRefConstraints(Expr *expr, Expr *base, DeclNameRef name, @@ -1792,7 +1802,11 @@ namespace { return Type(); } - return addSubscriptConstraints(expr, CS.getType(expr->getBase()), + auto *base = expr->getBase(); + if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) + return nullptr; + + return addSubscriptConstraints(expr, CS.getType(base), expr->getIndex(), decl, expr->getArgumentLabels(), expr->hasTrailingClosure()); @@ -2158,10 +2172,11 @@ namespace { } case PatternKind::Typed: { - auto typedPattern = cast(pattern); // FIXME: Need a better locator for a pattern as a base. - Type openedType = CS.openUnboundGenericType(typedPattern->getType(), - locator); + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, CurDC); + Type type = TypeChecker::typeCheckPattern(contextualPattern); + Type openedType = CS.openUnboundGenericType(type, locator); // For a typed pattern, simply return the opened type of the pattern. // FIXME: Error recovery if the type is an error type? @@ -2258,6 +2273,7 @@ namespace { // or exhaustive catches. class FindInnerThrows : public ASTWalker { ConstraintSystem &CS; + DeclContext *DC; bool FoundThrow = false; std::pair walkToExprPre(Expr *expr) override { @@ -2343,12 +2359,12 @@ namespace { Type exnType = CS.getASTContext().getErrorDecl()->getDeclaredType(); if (!exnType) return false; - if (TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(CS.DC), - exnType, - TypeResolverContext::InExpression)) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType( + contextualPattern, exnType, TypeResolverContext::InExpression); + if (!pattern) return false; - } clause->setErrorPattern(pattern); return clause->isSyntacticallyExhaustive(); @@ -2384,7 +2400,8 @@ namespace { } public: - FindInnerThrows(ConstraintSystem &cs) : CS(cs) {} + FindInnerThrows(ConstraintSystem &cs, DeclContext *dc) + : CS(cs), DC(dc) {} bool foundThrow() { return FoundThrow; } }; @@ -2397,7 +2414,7 @@ namespace { if (!body) return false; - auto tryFinder = FindInnerThrows(CS); + auto tryFinder = FindInnerThrows(CS, expr); body->walk(tryFinder); return tryFinder.foundThrow(); } @@ -2430,7 +2447,8 @@ namespace { CS.getConstraintLocator(expr, ConstraintLocator::ClosureResult); if (expr->hasEmptyBody()) { - resultTy = CS.createTypeVariable(locator, 0); + resultTy = CS.createTypeVariable( + locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole); // Closures with empty bodies should be inferred to return // (). @@ -2442,7 +2460,8 @@ namespace { } else { // If no return type was specified, create a fresh type // variable for it. - resultTy = CS.createTypeVariable(locator, 0); + resultTy = CS.createTypeVariable( + locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole); if (closureHasNoResult(expr)) { // Allow it to default to () if there are no return statements. @@ -2611,18 +2630,18 @@ namespace { CS.addConstraint( ConstraintKind::Conversion, CS.getType(expr->getCondExpr()), boolDecl->getDeclaredType(), - CS.getConstraintLocator(expr, LocatorPathElt::Condition())); + CS.getConstraintLocator(expr, ConstraintLocator::Condition)); // The branches must be convertible to a common type. - return CS.addJoinConstraint(CS.getConstraintLocator(expr), - { - { CS.getType(expr->getThenExpr()), - CS.getConstraintLocator(expr->getThenExpr()) }, - { CS.getType(expr->getElseExpr()), - CS.getConstraintLocator(expr->getElseExpr()) } - }); + return CS.addJoinConstraint( + CS.getConstraintLocator(expr), + {{CS.getType(expr->getThenExpr()), + CS.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))}, + {CS.getType(expr->getElseExpr()), + CS.getConstraintLocator(expr, + LocatorPathElt::TernaryBranch(false))}}); } - + virtual Type visitImplicitConversionExpr(ImplicitConversionExpr *expr) { llvm_unreachable("Already type-checked"); } @@ -3407,8 +3426,11 @@ namespace { // Restore '@autoclosure'd value. if (auto ACE = dyn_cast(expr)) { - expr = ACE->getSingleExpressionBody(); - continue; + // This is only valid if the closure doesn't have parameters. + if (ACE->getParameters()->size() == 0) { + expr = ACE->getSingleExpressionBody(); + continue; + } } // Remove any semantic expression injected by typechecking. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index bbea70f1a0f10..6a96c38e8c218 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -764,60 +764,6 @@ matchCallArguments(SmallVectorImpl &args, return listener.relabelArguments(actualArgNames); } -/// Find the callee declaration and uncurry level for a given call -/// locator. -static std::tuple, bool, - ConstraintLocator *> -getCalleeDeclAndArgs(ConstraintSystem &cs, - ConstraintLocatorBuilder callBuilder) { - auto formUnknownCallee = - []() -> std::tuple, bool, - ConstraintLocator *> { - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - /*argLabels*/ ArrayRef(), - /*hasTrailingClosure*/ false, - /*calleeLocator*/ nullptr); - }; - - auto *callLocator = cs.getConstraintLocator(callBuilder); - auto *callExpr = callLocator->getAnchor(); - - // Break down the call. - if (!callExpr) - return formUnknownCallee(); - - // Our remaining path can only be 'ApplyArgument'. - auto path = callLocator->getPath(); - if (!path.empty() && - !(path.size() <= 2 && - path.back().getKind() == ConstraintLocator::ApplyArgument)) - return formUnknownCallee(); - - // Dig out the callee information. - auto argInfo = cs.getArgumentInfo(callLocator); - if (!argInfo) - return formUnknownCallee(); - - auto argLabels = argInfo->Labels; - auto hasTrailingClosure = argInfo->HasTrailingClosure; - auto calleeLocator = cs.getCalleeLocator(callLocator); - - // Find the overload choice corresponding to the callee locator. - auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); - - // If we didn't find any matching overloads, we're done. Just return the - // argument info. - if (!selectedOverload) - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - argLabels, hasTrailingClosure, - /*calleeLocator*/ nullptr); - - // Return the found declaration, assuming there is one. - auto choice = selectedOverload->choice; - return std::make_tuple(choice.getDeclOrNull(), hasAppliedSelf(cs, choice), - argLabels, hasTrailingClosure, calleeLocator); -} - class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; SmallVectorImpl &Arguments; @@ -1004,22 +950,29 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArrayRef args, ArrayRef params, ConstraintKind subKind, ConstraintLocatorBuilder locator) { - // Extract the parameters. - ValueDecl *callee; - bool hasAppliedSelf; - ArrayRef argLabels; - bool hasTrailingClosure = false; - ConstraintLocator *calleeLocator; - std::tie(callee, hasAppliedSelf, argLabels, hasTrailingClosure, - calleeLocator) = - getCalleeDeclAndArgs(cs, locator); - - ParameterListInfo paramInfo(params, callee, hasAppliedSelf); + auto *loc = cs.getConstraintLocator(locator); + assert(loc->isLastElement()); + + ValueDecl *callee = nullptr; + bool appliedSelf = false; + + // Resolve the callee for the application. + auto *calleeLocator = cs.getCalleeLocator(loc); + if (auto overload = cs.findSelectedOverloadFor(calleeLocator)) { + callee = overload->choice.getDeclOrNull(); + appliedSelf = hasAppliedSelf(cs, overload->choice); + } + + ParameterListInfo paramInfo(params, callee, appliedSelf); + + // Dig out the argument information. + auto argInfo = cs.getArgumentInfo(loc); + assert(argInfo); // Apply labels to arguments. SmallVector argsWithLabels; argsWithLabels.append(args.begin(), args.end()); - AnyFunctionType::relabelParams(argsWithLabels, argLabels); + AnyFunctionType::relabelParams(argsWithLabels, argInfo->Labels); // Special case when a single tuple argument if used // instead of N distinct arguments e.g.: @@ -1061,7 +1014,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings, locator); if (constraints::matchCallArguments( - argsWithLabels, params, paramInfo, hasTrailingClosure, + argsWithLabels, params, paramInfo, argInfo->HasTrailingClosure, cs.shouldAttemptFixes(), listener, parameterBindings)) return cs.getTypeMatchFailure(locator); @@ -1147,9 +1100,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( = paramInfo.getFunctionBuilderType(paramIdx)) { Expr *arg = getArgumentExpr(locator.getAnchor(), argIdx); if (auto closure = dyn_cast_or_null(arg)) { - auto result = - cs.applyFunctionBuilder(closure, functionBuilderType, - calleeLocator, loc); + auto closureType = cs.getType(closure); + auto result = cs.matchFunctionBuilder( + closure, functionBuilderType, + closureType->castTo()->getResult(), + ConstraintKind::Conversion, calleeLocator, loc); if (result.isFailure()) return result; } @@ -1254,6 +1209,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: @@ -1318,6 +1274,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: @@ -1596,6 +1553,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: @@ -2199,8 +2157,18 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, } } else { // There are no elements in the path auto *anchor = locator.getAnchor(); - if (!(anchor && isa(anchor))) + if (!(anchor && + (isa(anchor) || isa(anchor)))) + return getTypeMatchFailure(locator); + } + + auto *anchor = locator.getAnchor(); + if (isa(anchor)) { + auto *fix = ContextualMismatch::create( + *this, type1, type2, getConstraintLocator(locator)); + if (recordFix(fix)) return getTypeMatchFailure(locator); + break; } auto *fix = MissingConformance::forContextual( @@ -2803,6 +2771,13 @@ bool ConstraintSystem::repairFailures( }); }; + auto markAnyTypeVarsAsPotentialHoles = [&](Type type) { + type.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + }; + if (path.empty()) { if (!anchor) return false; @@ -2822,6 +2797,21 @@ bool ConstraintSystem::repairFailures( conversionsOrFixes.push_back(coerceToCheckCastFix); return true; } + + // If it has a deep equality restriction, defer the diagnostic to + // GenericMismatch. + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) && + !hasConversionOrRestriction( + ConversionRestrictionKind::OptionalToOptional)) { + return false; + } + + if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) + return false; + + auto *fix = ContextualMismatch::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); } // This could be: @@ -3475,6 +3465,13 @@ bool ConstraintSystem::repairFailures( break; } } + // Handle function result coerce expression wrong type conversion. + if (anchor && isa(anchor)) { + auto *fix = + ContextualMismatch::create(*this, lhs, rhs, loc); + conversionsOrFixes.push_back(fix); + break; + } LLVM_FALLTHROUGH; } @@ -3653,6 +3650,33 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::TernaryBranch: { + markAnyTypeVarsAsPotentialHoles(lhs); + markAnyTypeVarsAsPotentialHoles(rhs); + + // If `if` expression has a contextual type, let's consider it a source of + // truth and produce a contextual mismatch instead of per-branch failure, + // because it's a better pointer than potential then-to-else type mismatch. + if (auto contextualType = getContextualType(anchor)) { + if (contextualType->isEqual(rhs)) { + auto *loc = + getConstraintLocator(anchor, LocatorPathElt::ContextualType()); + if (hasFixFor(loc, FixKind::ContextualMismatch)) + return true; + + conversionsOrFixes.push_back( + ContextualMismatch::create(*this, lhs, rhs, loc)); + break; + } + } + + // If there is no contextual type, this is most likely a contextual type + // mismatch between then/else branches of ternary operator. + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + default: break; } @@ -3865,6 +3889,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: @@ -4014,9 +4039,20 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, subKind = ConstraintKind::Bind; } - return matchTypes( - instanceType1, instanceType2, subKind, subflags, - locator.withPathElement(ConstraintLocator::InstanceType)); + auto result = + matchTypes(instanceType1, instanceType2, subKind, subflags, + locator.withPathElement(ConstraintLocator::InstanceType)); + if (shouldAttemptFixes() && result.isFailure()) { + auto *anchor = locator.getAnchor(); + if (anchor && isa(anchor)) { + auto *fix = + ContextualMismatch::create(*this, instanceType1, instanceType2, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + } + return result; } case TypeKind::Function: { @@ -6081,7 +6117,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( markMemberTypeAsPotentialHole(memberTy); return SolutionKind::Solved; - } else if (kind == ConstraintKind::ValueMember && baseObjTy->isHole()) { + } else if ((kind == ConstraintKind::ValueMember || + kind == ConstraintKind::ValueWitness) && + baseObjTy->isHole()) { // If base type is a "hole" there is no reason to record any // more "member not found" fixes for chained member references. increaseScore(SK_Fix); @@ -6117,8 +6155,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return formUnsolved(); case MemberLookupResult::ErrorAlreadyDiagnosed: - return SolutionKind::Error; - case MemberLookupResult::HasResults: // Keep going! break; @@ -6194,8 +6230,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return type; }); - auto *fix = - DefineMemberBasedOnUse::create(*this, baseTy, member, locator); + bool alreadyDiagnosed = (result.OverallResult == + MemberLookupResult::ErrorAlreadyDiagnosed); + auto *fix = DefineMemberBasedOnUse::create(*this, baseTy, member, + alreadyDiagnosed, locator); // Impact is higher if the base is expected to be inferred from context, // because a failure to find a member ultimately means that base type is // not a match in this case. @@ -6204,9 +6242,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (recordFix(fix, impact)) return SolutionKind::Error; - // Allow member type to default to `Any` to make it possible to form - // solutions when contextual type of the result cannot be deduced e.g. - // `let _ = x.foo`. + // Record a hole for memberTy to make it possible to form solutions + // when contextual result type cannot be deduced e.g. `let _ = x.foo`. if (auto *memberTypeVar = memberTy->getAs()) recordPotentialHole(memberTypeVar); @@ -6356,6 +6393,72 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return SolutionKind::Error; } +ConstraintSystem::SolutionKind +ConstraintSystem::simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + // We'd need to record original base type because it might be a type + // variable representing another missing member. + auto origBaseType = baseType; + + auto formUnsolved = [&] { + // If requested, generate a constraint. + if (flags.contains(TMF_GenerateConstraints)) { + auto *witnessConstraint = Constraint::createValueWitness( + *this, kind, origBaseType, memberType, requirement, useDC, + functionRefKind, getConstraintLocator(locator)); + + addUnsolvedConstraint(witnessConstraint); + return SolutionKind::Solved; + } + + return SolutionKind::Unsolved; + }; + + // Resolve the base type, if we can. If we can't resolve the base type, + // then we can't solve this constraint. + Type baseObjectType = getFixedTypeRecursive( + baseType, flags, /*wantRValue=*/true); + if (baseObjectType->isTypeVariableOrMember()) { + return formUnsolved(); + } + + // Check conformance to the protocol. If it doesn't conform, this constraint + // fails. Don't attempt to fix it. + // FIXME: Look in the constraint system to see if we've resolved the + // conformance already? + auto proto = requirement->getDeclContext()->getSelfProtocolDecl(); + assert(proto && "Value witness constraint for a non-requirement"); + auto conformance = TypeChecker::conformsToProtocol( + baseObjectType, proto, useDC, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (!conformance) { + // The conformance failed, so mark the member type as a "hole". We cannot + // do anything further here. + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + memberType.visit([&](Type type) { + if (auto *typeVar = type->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + + // Reference the requirement. + Type resolvedBaseType = simplifyType(baseType, flags); + if (resolvedBaseType->isTypeVariableOrMember()) + return formUnsolved(); + + auto choice = OverloadChoice(resolvedBaseType, requirement, functionRefKind); + resolveOverload(getConstraintLocator(locator), memberType, choice, + useDC); + return SolutionKind::Solved; +} + ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( Type first, Type second, @@ -6397,6 +6500,16 @@ ConstraintSystem::simplifyOneWayConstraint( return SolutionKind::Unsolved; } + // Propagate holes through one-way constraints. + if (secondSimplified->isHole()) { + first.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + // Translate this constraint into a one-way binding constraint. return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, locator); @@ -7376,8 +7489,9 @@ ConstraintSystem::simplifyApplicableFnConstraint( } // If right-hand side is a type variable, the constraint is unsolved. - if (desugar2->isTypeVariableOrMember()) + if (desugar2->isTypeVariableOrMember()) { return formUnsolved(); + } // Strip the 'ApplyFunction' off the locator. // FIXME: Perhaps ApplyFunction can go away entirely? @@ -7393,18 +7507,16 @@ ConstraintSystem::simplifyApplicableFnConstraint( // is true. if (desugar2->isCallableNominalType(DC)) { auto memberLoc = getConstraintLocator( - outerLocator.withPathElement(ConstraintLocator::Member)); + locator.withPathElement(ConstraintLocator::ImplicitCallAsFunction)); // Add a `callAsFunction` member constraint, binding the member type to a // type variable. auto memberTy = createTypeVariable(memberLoc, /*options=*/0); // TODO: Revisit this if `static func callAsFunction` is to be supported. // Static member constraint requires `FunctionRefKind::DoubleApply`. - // TODO: Use a custom locator element to identify this member constraint - // instead of just pointing to the function expr. addValueMemberConstraint(origLValueType2, DeclNameRef(ctx.Id_callAsFunction), memberTy, DC, FunctionRefKind::SingleApply, - /*outerAlternatives*/ {}, locator); + /*outerAlternatives*/ {}, memberLoc); // Add new applicable function constraint based on the member type // variable. addConstraint(ConstraintKind::ApplicableFunction, func1, memberTy, @@ -7427,6 +7539,26 @@ ConstraintSystem::simplifyApplicableFnConstraint( // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } + + // Let's account for optional members concept from Objective-C + // which forms a disjunction for member type to check whether + // it would be possible to use optional type directly or it has + // to be force unwrapped (because such types are imported as IUO). + if (unwrapCount > 0 && desugar2->is()) { + auto *typeVar = desugar2->castTo(); + auto *locator = typeVar->getImpl().getLocator(); + if (locator->isLastElement()) { + auto *fix = ForceOptional::create(*this, origType2, desugar2, + getConstraintLocator(locator)); + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; + + // Since the right-hand side of the constraint has been changed + // we have to re-generate this constraint to use new type. + flags |= TMF_GenerateConstraints; + return formUnsolved(); + } + } } // For a function, bind the output and convert the argument to the input. @@ -7500,11 +7632,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( desugar2->is()) return SolutionKind::Error; - if (auto objectTy = desugar2->lookThroughAllOptionalTypes()) { - if (objectTy->isAny() || objectTy->isAnyObject()) - return SolutionKind::Error; - } - // If there are any type variables associated with arguments/result // they have to be marked as "holes". type1.visit([&](Type subType) { @@ -7777,7 +7904,7 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( DeclNameRef memberName({ ctx, ctx.Id_dynamicallyCall, {argLabel} }); auto *fix = DefineMemberBasedOnUse::create( - *this, desugar2, memberName, + *this, desugar2, memberName, /*alreadyDiagnosed=*/false, getConstraintLocator(loc, ConstraintLocator::DynamicCallable)); if (recordFix(fix)) @@ -8535,7 +8662,24 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( } case FixKind::ContextualMismatch: { - if (recordFix(fix)) + auto impact = 1; + + auto locator = fix->getLocator(); + if (auto branchElt = + locator->getLastElementAs()) { + // If this is `else` branch of a ternary operator, let's + // increase its impact to eliminate the chance of ambiguity. + // + // Branches are connected through two `subtype` constraints + // to a common type variable with represents their join, which + // means that result would attempt a type from each side if + // one is available and that would result in two fixes - one for + // each mismatched branch. + if (branchElt->forElse()) + impact = 10; + } + + if (recordFix(fix, impact)) return SolutionKind::Error; if (auto *fnType1 = type1->getAs()) { @@ -8583,6 +8727,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: + case FixKind::SpecifyClosureReturnType: llvm_unreachable("handled elsewhere"); } @@ -8659,6 +8804,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BindOverload: case ConstraintKind::Disjunction: case ConstraintKind::KeyPath: @@ -9046,6 +9192,16 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { TMF_GenerateConstraints, constraint.getLocator()); + case ConstraintKind::ValueWitness: + return simplifyValueWitnessConstraint(constraint.getKind(), + constraint.getFirstType(), + constraint.getRequirement(), + constraint.getSecondType(), + constraint.getMemberUseDC(), + constraint.getFunctionRefKind(), + TMF_GenerateConstraints, + constraint.getLocator()); + case ConstraintKind::Defaultable: return simplifyDefaultableConstraint(constraint.getFirstType(), constraint.getSecondType(), diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index a592219e498d3..5f26aa246e5b7 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -171,13 +171,13 @@ Solution ConstraintSystem::finalize() { for (auto &e : CheckedConformances) solution.Conformances.push_back({e.first, e.second}); - for (const auto &transformed : builderTransformedClosures) { + for (const auto &transformed : functionBuilderTransformed) { auto known = - solution.builderTransformedClosures.find(transformed.first); - if (known != solution.builderTransformedClosures.end()) { + solution.functionBuilderTransformed.find(transformed.first); + if (known != solution.functionBuilderTransformed.end()) { assert(known->second.singleExpr == transformed.second.singleExpr); } - solution.builderTransformedClosures.insert(transformed); + solution.functionBuilderTransformed.insert(transformed); } return solution; @@ -241,8 +241,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (auto &conformance : solution.Conformances) CheckedConformances.push_back(conformance); - for (const auto &transformed : solution.builderTransformedClosures) { - builderTransformedClosures.push_back(transformed); + for (const auto &transformed : solution.functionBuilderTransformed) { + functionBuilderTransformed.push_back(transformed); } // Register any fixes produced along this path. @@ -436,7 +436,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); - numBuilderTransformedClosures = cs.builderTransformedClosures.size(); + numFunctionBuilderTransformed = cs.functionBuilderTransformed.size(); numResolvedOverloads = cs.ResolvedOverloads.size(); PreviousScore = cs.CurrentScore; @@ -503,7 +503,7 @@ ConstraintSystem::SolverScope::~SolverScope() { truncate(cs.CheckedConformances, numCheckedConformances); /// Remove any builder transformed closures. - truncate(cs.builderTransformedClosures, numBuilderTransformedClosures); + truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); // Reset the previous score. cs.CurrentScore = PreviousScore; @@ -1127,7 +1127,7 @@ bool ConstraintSystem::solve(Expr *&expr, return true; case SolutionResult::Kind::UndiagnosedError: - diagnoseFailureForExpr(expr); + diagnoseFailureFor(expr); salvagedResult.markAsDiagnosed(); return true; @@ -1670,6 +1670,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::BindToPointerType: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Disjunction: case ConstraintKind::CheckedCast: diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 35139a903e797..9556bc555a660 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1002,13 +1002,18 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { llvm::Expected InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, ClassDecl *decl) const { + // Check if we parsed the @_inheritsConvenienceInitializers attribute. + if (decl->getAttrs().hasAttribute()) + return true; + auto superclass = decl->getSuperclass(); assert(superclass); // If the superclass has known-missing designated initializers, inheriting // is unsafe. auto *superclassDecl = superclass->getClassOrBoundGenericClass(); - if (superclassDecl->hasMissingDesignatedInitializers()) + if (superclassDecl->getModuleContext() != decl->getParentModule() && + superclassDecl->hasMissingDesignatedInitializers()) return false; // If we're allowed to inherit designated initializers, then we can inherit diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 0a41efe4197de..a70513e263391 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -77,6 +77,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: llvm_unreachable("Wrong constructor for member constraint"); case ConstraintKind::Defaultable: @@ -127,6 +128,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Defaultable: case ConstraintKind::BindOverload: @@ -156,7 +158,7 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second, ArrayRef typeVars) : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), RememberChoice(false), IsFavored(false), - NumTypeVariables(typeVars.size()), Member{first, second, member, useDC}, + NumTypeVariables(typeVars.size()), Member{first, second, {member}, useDC}, Locator(locator) { assert(kind == ConstraintKind::ValueMember || kind == ConstraintKind::UnresolvedValueMember); @@ -168,6 +170,28 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second, std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); } +Constraint::Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, + ConstraintLocator *locator, + ArrayRef typeVars) + : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), + RememberChoice(false), IsFavored(false), + NumTypeVariables(typeVars.size()), Locator(locator) { + Member.First = first; + Member.Second = second; + Member.Member.Ref = requirement; + Member.UseDC = useDC; + TheFunctionRefKind = static_cast(functionRefKind); + + assert(kind == ConstraintKind::ValueWitness); + assert(getFunctionRefKind() == functionRefKind); + assert(requirement && "Value witness constraint has no requirement"); + assert(useDC && "Member constraint has no use DC"); + + std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); +} + Constraint::Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, ArrayRef typeVars) @@ -251,6 +275,11 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { getMember(), getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::ValueWitness: + return createValueWitness( + cs, getKind(), getFirstType(), getSecondType(), getRequirement(), + getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::Disjunction: return createDisjunction(cs, getNestedConstraints(), getLocator()); @@ -386,11 +415,20 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } case ConstraintKind::ValueMember: - Out << "[." << Member.Member << ": value] == "; + Out << "[." << getMember() << ": value] == "; break; case ConstraintKind::UnresolvedValueMember: - Out << "[(implicit) ." << Member.Member << ": value] == "; + Out << "[(implicit) ." << getMember() << ": value] == "; + break; + + case ConstraintKind::ValueWitness: { + auto requirement = getRequirement(); + auto selfNominal = requirement->getDeclContext()->getSelfNominalTypeDecl(); + Out << "[." << selfNominal->getName() << "::" << requirement->getFullName() + << ": witness] == "; break; + } + case ConstraintKind::Defaultable: Out << " can default to "; break; @@ -510,6 +548,7 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::Subtype: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: @@ -648,6 +687,27 @@ Constraint *Constraint::createMember(ConstraintSystem &cs, ConstraintKind kind, functionRefKind, locator, typeVars); } +Constraint *Constraint::createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator) { + assert(kind == ConstraintKind::ValueWitness); + + // Collect type variables. + SmallVector typeVars; + if (first->hasTypeVariable()) + first->getTypeVariables(typeVars); + if (second->hasTypeVariable()) + second->getTypeVariables(typeVars); + uniqueTypeVariables(typeVars); + + // Create the constraint. + unsigned size = totalSizeToAlloc(typeVars.size()); + void *mem = cs.getAllocator().Allocate(size, alignof(Constraint)); + return new (mem) Constraint(kind, first, second, requirement, useDC, + functionRefKind, locator, typeVars); +} + Constraint *Constraint::createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, DeclContext *useDC, diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 6af00f357f274..83898a76a7b77 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -116,6 +116,11 @@ enum class ConstraintKind : char { /// name, and the type of that member, when referenced as a value, is the /// second type. UnresolvedValueMember, + /// The first type conforms to the protocol in which the member requirement + /// resides. Once the conformance is resolved, the value witness will be + /// determined, and the type of that witness, when referenced as a value, + /// will be bound to the second type. + ValueWitness, /// The first type can be defaulted to the second (which currently /// cannot be dependent). This is more like a type property than a /// relational constraint. @@ -315,9 +320,18 @@ class Constraint final : public llvm::ilist_node, /// The type of the member. Type Second; - /// If non-null, the name of a member of the first type is that - /// being related to the second type. - DeclNameRef Member; + union { + /// If non-null, the name of a member of the first type is that + /// being related to the second type. + /// + /// Used for ValueMember an UnresolvedValueMember constraints. + DeclNameRef Name; + + /// If non-null, the member being referenced. + /// + /// Used for ValueWitness constraints. + ValueDecl *Ref; + } Member; /// The DC in which the use appears. DeclContext *UseDC; @@ -365,6 +379,12 @@ class Constraint final : public llvm::ilist_node, ConstraintLocator *locator, ArrayRef typeVars); + /// Construct a new value witness constraint. + Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator, + ArrayRef typeVars); + /// Construct a new overload-binding constraint, which might have a fix. Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, @@ -410,6 +430,12 @@ class Constraint final : public llvm::ilist_node, FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create a new value witness constraint. + static Constraint *createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create an overload-binding constraint. static Constraint *createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, @@ -518,6 +544,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return ConstraintClassification::Member; case ConstraintKind::DynamicTypeOf: @@ -548,6 +575,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.First; default: @@ -564,6 +592,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.Second; default: @@ -589,19 +618,20 @@ class Constraint final : public llvm::ilist_node, DeclNameRef getMember() const { assert(Kind == ConstraintKind::ValueMember || Kind == ConstraintKind::UnresolvedValueMember); - return Member.Member; + return Member.Member.Name; } - /// Determine whether this constraint kind has a second type. - static bool hasMember(ConstraintKind kind) { - return kind == ConstraintKind::ValueMember - || kind == ConstraintKind::UnresolvedValueMember; + /// Retrieve the requirement being referenced by a value witness constraint. + ValueDecl *getRequirement() const { + assert(Kind == ConstraintKind::ValueWitness); + return Member.Member.Ref; } /// Determine the kind of function reference we have for a member reference. FunctionRefKind getFunctionRefKind() const { if (Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember) + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness) return static_cast(TheFunctionRefKind); // Conservative answer: drop all of the labels. @@ -647,7 +677,8 @@ class Constraint final : public llvm::ilist_node, /// Retrieve the DC in which the member was used. DeclContext *getMemberUseDC() const { assert(Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember); + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness); return Member.UseDC; } diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 8c4801dce58d6..bba1582f764df 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -61,7 +61,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case ConditionalRequirement: case TypeParameterRequirement: case ContextualType: - case SynthesizedArgument: { + case SynthesizedArgument: + case TernaryBranch: { auto numValues = numNumericValuesInPathElement(elt.getKind()); for (unsigned i = 0; i < numValues; ++i) id.AddInteger(elt.getValue(i)); @@ -69,8 +70,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, } #define SIMPLE_LOCATOR_PATH_ELT(Name) case Name : #include "ConstraintLocatorPathElts.def" - // Nothing to do for simple locator elements. - break; + // Nothing to do for simple locator elements. + break; } } } @@ -114,6 +115,8 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::KeyPathComponentResult: case ConstraintLocator::Condition: case ConstraintLocator::DynamicCallable: + case ConstraintLocator::ImplicitCallAsFunction: + case ConstraintLocator::TernaryBranch: return 0; case ConstraintLocator::FunctionArgument: @@ -458,6 +461,16 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { case DynamicCallable: out << "implicit call to @dynamicCallable method"; break; + + case ImplicitCallAsFunction: + out << "implicit reference to callAsFunction"; + break; + + case TernaryBranch: + auto branchElt = elt.castTo(); + out << (branchElt.forThen() ? "'then'" : "'else'") + << " branch of a ternary operator"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index 9939a878c52b3..e7cfd9eaf2132 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -78,6 +78,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case KeyPathComponent: case SynthesizedArgument: case KeyPathDynamicMember: + case TernaryBranch: return 1; case TypeParameterRequirement: @@ -782,6 +783,20 @@ class LocatorPathElt::KeyPathDynamicMember final : public LocatorPathElt { } }; +class LocatorPathElt::TernaryBranch final : public LocatorPathElt { +public: + TernaryBranch(bool side) + : LocatorPathElt(ConstraintLocator::TernaryBranch, side) {} + + bool forThen() const { return bool(getValue(0)); } + + bool forElse() const { return !bool(getValue(0)); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::TernaryBranch; + } +}; + /// A simple stack-only builder object that constructs a /// constraint locator without allocating memory. /// diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 631216485e037..54912bbfa9953 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -76,6 +76,9 @@ SIMPLE_LOCATOR_PATH_ELT(FunctionResult) /// FIXME: Add support for named generic arguments? CUSTOM_LOCATOR_PATH_ELT(GenericArgument) +/// An implicit reference to a 'callAsFunction' method of a nominal type. +SIMPLE_LOCATOR_PATH_ELT(ImplicitCallAsFunction) + /// Locator for a binding from an IUO disjunction choice. SIMPLE_LOCATOR_PATH_ELT(ImplicitlyUnwrappedDisjunctionChoice) @@ -169,6 +172,9 @@ SIMPLE_LOCATOR_PATH_ELT(Condition) SIMPLE_LOCATOR_PATH_ELT(DynamicCallable) +/// The 'true' or 'false' branch of a ternary operator. +CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a0ef70251cb2d..aefad4f372b0d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -438,6 +438,18 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator, auto *anchor = locator->getAnchor(); assert(anchor && "Expected an anchor!"); + auto path = locator->getPath(); + { + // If we have a locator for a member found through key path dynamic member + // lookup, then we need to chop off the elements after the + // KeyPathDynamicMember element to get the callee locator. + auto iter = path.rbegin(); + if (locator->findLast(iter)) { + auto newPath = path.drop_back(iter - path.rbegin()); + return getConstraintLocator(anchor, newPath); + } + } + // If we have a locator that starts with a key path component element, we // may have a callee given by a property or subscript component. if (auto componentElt = @@ -494,9 +506,11 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator, } // Handle an apply of a nominal type which supports callAsFunction. - if (fnTy->isCallableNominalType(DC)) - return getConstraintLocator(anchor, ConstraintLocator::ApplyFunction); - + if (fnTy->isCallableNominalType(DC)) { + return getConstraintLocator(anchor, + {LocatorPathElt::ApplyFunction(), + LocatorPathElt::ImplicitCallAsFunction()}); + } return nullptr; }; @@ -2152,8 +2166,10 @@ void ConstraintSystem::bindOverloadType( auto adjustedFnTy = FunctionType::get(fnType->getParams(), subscriptResultTy); - addConstraint(ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy, - applicableFn->getLocator()); + ConstraintLocatorBuilder kpLocBuilder(keyPathLoc); + addConstraint( + ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy, + kpLocBuilder.withPathElement(ConstraintLocator::ApplyFunction)); addConstraint(ConstraintKind::Bind, dynamicResultTy, fnType->getResult(), keyPathLoc); @@ -2734,34 +2750,52 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // overloads of the same declaration. ConstraintLocator *commonCalleeLocator = nullptr; SmallPtrSet distinctChoices; - SmallVector, 4> - viableSolutions; + SmallVector viableSolutions; + + enum class AmbiguityKind { + /// There is exactly one fix associated with each candidate. + CloseMatch, + /// Solution is ambiguous because all candidates had partially matching + /// parameter lists. + ParameterList, + /// General ambiguity failure. + General, + }; + auto ambiguityKind = AmbiguityKind::CloseMatch; bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) { ArrayRef fixes = solution.Fixes; - // Currently only support a single fix in a solution, - // but ultimately should be able to deal with multiple. - if (fixes.size() != 1) + if (fixes.empty()) return false; - const auto *fix = fixes.front(); - auto *locator = fix->getLocator(); + if (fixes.size() > 1) { + ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch || + ambiguityKind == AmbiguityKind::ParameterList) && + llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool { + auto *locator = fix->getLocator(); + return locator->findLast().hasValue(); + }) ? AmbiguityKind::ParameterList + : AmbiguityKind::General; + } + + for (const auto *fix: fixes) { + auto *locator = fix->getLocator(); + // Assignment failures are all about the source expression, + // because they treat destination as a contextual type. + if (auto *anchor = locator->getAnchor()) { + if (auto *assignExpr = dyn_cast(anchor)) + locator = getConstraintLocator(assignExpr->getSrc()); + } - // Assignment failures are all about the source expression, - // because they treat destination as a contextual type. - if (auto *anchor = locator->getAnchor()) { - if (auto *assignExpr = dyn_cast(anchor)) - locator = getConstraintLocator(assignExpr->getSrc()); + auto *calleeLocator = getCalleeLocator(locator); + if (!commonCalleeLocator) + commonCalleeLocator = calleeLocator; + else if (commonCalleeLocator != calleeLocator) + return false; } - auto *calleeLocator = getCalleeLocator(locator); - if (commonCalleeLocator && commonCalleeLocator != calleeLocator) - return false; - - commonCalleeLocator = calleeLocator; - - auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator); if (!overload) return false; @@ -2773,7 +2807,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // as viable, otherwise we'd produce the same diagnostic multiple // times, which means that actual problem is elsewhere. if (distinctChoices.insert(decl).second) - viableSolutions.push_back({&solution, fix}); + viableSolutions.push_back(&solution); return true; }); @@ -2787,32 +2821,11 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( { DiagnosticTransaction transaction(getASTContext().Diags); - const auto *fix = viableSolutions.front().second; auto *commonAnchor = commonCalleeLocator->getAnchor(); auto &DE = getASTContext().Diags; auto name = decl->getFullName(); - if (fix->getKind() == FixKind::UseSubscriptOperator) { - auto *UDE = cast(commonAnchor); - DE.diagnose(commonAnchor->getLoc(), - diag::could_not_find_subscript_member_did_you_mean, - getType(UDE->getBase())); - } else if (fix->getKind() == FixKind::TreatRValueAsLValue) { - DE.diagnose(commonAnchor->getLoc(), - diag::no_overloads_match_exactly_in_assignment, - decl->getBaseName()); - } else if (llvm::all_of( - viableSolutions, - [](const std::pair - &fix) { - auto *locator = fix.second->getLocator(); - return locator - ->isLastElement(); - })) { - auto baseName = name.getBaseName(); - DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type, - baseName.userFacingName(), getContextualType()); - } else { + auto emitGeneralAmbiguityFailure = [&]() { // Three choices here: // 1. If this is a special name avoid printing it because // printing kind is sufficient; @@ -2839,14 +2852,80 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( diag::no_overloads_match_exactly_in_call_no_labels, decl->getDescriptiveKind(), name.getBaseName()); } + }; + + switch (ambiguityKind) { + case AmbiguityKind::CloseMatch: + // Handled below + break; + case AmbiguityKind::ParameterList: { + emitGeneralAmbiguityFailure(); + + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *fn = overload.openedType->getAs(); + assert(fn); + DE.diagnose(overload.choice.getDecl()->getLoc(), + diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + } + + return true; + } + case AmbiguityKind::General: { + emitGeneralAmbiguityFailure(); + + // Notes for operators are diagnosed through emitGeneralAmbiguityFailure + if (name.isOperator()) + return true; + + llvm::SmallSet candidateTypes; + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *decl = overload.choice.getDecl(); + auto type = viable->simplifyType(overload.openedType); + if (decl->getLoc().isInvalid()) { + if (candidateTypes.insert(type->getCanonicalType()).second) + DE.diagnose(commonAnchor->getLoc(), diag::found_candidate_type, type); + } else { + DE.diagnose(decl->getLoc(), diag::found_candidate); + } + } + + return true; + } + } + + auto *fix = viableSolutions.front()->Fixes.front(); + if (fix->getKind() == FixKind::UseSubscriptOperator) { + auto *UDE = cast(commonAnchor); + DE.diagnose(commonAnchor->getLoc(), + diag::could_not_find_subscript_member_did_you_mean, + getType(UDE->getBase())); + } else if (fix->getKind() == FixKind::TreatRValueAsLValue) { + DE.diagnose(commonAnchor->getLoc(), + diag::no_overloads_match_exactly_in_assignment, + decl->getBaseName()); + } else if (llvm::all_of( + viableSolutions, + [](const Solution *viable) { + auto *locator = viable->Fixes.front()->getLocator(); + return locator + ->isLastElement(); + })) { + auto baseName = name.getBaseName(); + DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type, + baseName.userFacingName(), getContextualType()); + } else { + emitGeneralAmbiguityFailure(); } for (const auto &viable : viableSolutions) { // Create scope so each applied solution is rolled back. ConstraintSystem::SolverScope scope(*this); - applySolution(*viable.first); + applySolution(*viable); // All of the solutions supposed to produce a "candidate" note. - diagnosed &= viable.second->diagnose(/*asNote*/ true); + diagnosed &= viable->Fixes.front()->diagnose(/*asNote*/ true); } // If not all of the fixes produced a note, we can't diagnose this. @@ -3131,8 +3210,9 @@ void constraints::simplifyLocator(Expr *&anchor, case ConstraintLocator::LValueConversion: case ConstraintLocator::RValueAdjustment: case ConstraintLocator::UnresolvedMember: - // Arguments in autoclosure positions, lvalue and rvalue adjustments, and - // scalar-to-tuple conversions, and unresolved members are + case ConstraintLocator::ImplicitCallAsFunction: + // Arguments in autoclosure positions, lvalue and rvalue adjustments, + // unresolved members, and implicit callAsFunction references are // implicit. path = path.slice(1); continue; @@ -3252,6 +3332,15 @@ void constraints::simplifyLocator(Expr *&anchor, continue; } + case ConstraintLocator::TernaryBranch: { + auto branch = path[0].castTo(); + auto *ifExpr = cast(anchor); + + anchor = branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr(); + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; @@ -3424,6 +3513,18 @@ ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) { if (auto *UME = dyn_cast(anchor)) return getConstraintLocator(UME); + auto path = locator->getPath(); + { + // If this is for a dynamic member reference, the argument info is for the + // original call-site, which we can get by stripping away the + // KeyPathDynamicMember elements. + auto iter = path.begin(); + if (locator->findFirst(iter)) { + ArrayRef newPath(path.begin(), iter); + return getConstraintLocator(anchor, newPath); + } + } + return getCalleeLocator(locator); } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 493dc657f4246..4e4610ea32f73 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -297,6 +297,9 @@ class TypeVariableType::Implementation { /// Retrieve the generic parameter opened by this type variable. GenericTypeParamType *getGenericParameter() const; + /// Determine whether this type variable represents a closure result type. + bool isClosureResultType() const; + /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// @@ -471,19 +474,19 @@ enum class SolutionCompareResult { /// declaration was opened, which may involve type variables. struct SelectedOverload { /// The overload choice. - OverloadChoice choice; + const OverloadChoice choice; /// The opened type of the base of the reference to this overload, if /// we're referencing a member. - Type openedFullType; + const Type openedFullType; /// The opened type produced by referring to this overload. - Type openedType; + const Type openedType; /// The type that this overload binds. Note that this may differ from /// openedType, for example it will include any IUO unwrapping that has taken /// place. - Type boundType; + const Type boundType; }; /// Provides information about the application of a function argument to a @@ -665,6 +668,10 @@ struct AppliedBuilderTransform { /// The single expression to which the closure was transformed. Expr *singleExpr; + + /// The result type of the body, to which the returned expression will be + /// converted. + Type bodyResultType; }; /// Describes the fixed score of a solution to the constraint system. @@ -820,9 +827,9 @@ class Solution { std::vector> Conformances; - /// The set of closures that have been transformed by a function builder. - llvm::MapVector - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + llvm::MapVector + functionBuilderTransformed; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -895,6 +902,11 @@ class Solution { return None; } + /// Retrieve a fully-resolved protocol conformance at the given locator + /// and with the given protocol. + ProtocolConformanceRef resolveConformance(ConstraintLocator *locator, + ProtocolDecl *proto); + void setExprTypes(Expr *expr) const; SWIFT_DEBUG_DUMP; @@ -1092,6 +1104,54 @@ struct DynamicCallableMethods { } }; +/// Describes the target to which a constraint system's solution can be +/// applied. +class SolutionApplicationTarget { + enum class Kind { + expression, + function + } kind; + + union { + Expr *expression; + AnyFunctionRef function; + }; + +public: + SolutionApplicationTarget(Expr *expr) { + kind = Kind::expression; + expression = expr; + } + + SolutionApplicationTarget(AnyFunctionRef fn) { + kind = Kind::function; + function = fn; + } + + Expr *getAsExpr() const { + switch (kind) { + case Kind::expression: + return expression; + + case Kind::function: + return nullptr; + } + } + + Optional getAsFunction() const { + switch (kind) { + case Kind::expression: + return None; + + case Kind::function: + return function; + } + } + + /// Walk the contents of the application target. + llvm::PointerUnion walk(ASTWalker &walker); +}; + enum class ConstraintSystemPhase { ConstraintGeneration, Solving, @@ -1285,9 +1345,9 @@ class ConstraintSystem { std::vector> CheckedConformances; - /// The set of closures that have been transformed by a function builder. - std::vector> - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + std::vector> + functionBuilderTransformed; public: /// The locators of \c Defaultable constraints whose defaults were used. @@ -1784,7 +1844,7 @@ class ConstraintSystem { unsigned numFavoredConstraints; - unsigned numBuilderTransformedClosures; + unsigned numFunctionBuilderTransformed; /// The length of \c ResolvedOverloads. unsigned numResolvedOverloads; @@ -2235,12 +2295,12 @@ class ConstraintSystem { /// system to generate a plausible diagnosis of why the system could not be /// solved. /// - /// \param expr The expression whose constraints we're investigating for a - /// better diagnostic. + /// \param target The solution target whose constraints we're investigating + /// for a better diagnostic. /// /// Assuming that this constraint system is actually erroneous, this *always* /// emits an error message. - void diagnoseFailureForExpr(Expr *expr); + void diagnoseFailureFor(SolutionApplicationTarget target); bool diagnoseAmbiguity(ArrayRef solutions); bool diagnoseAmbiguityWithFixes(SmallVectorImpl &solutions); @@ -2364,6 +2424,26 @@ class ConstraintSystem { } } + /// Add a value witness constraint to the constraint system. + void addValueWitnessConstraint( + Type baseTy, ValueDecl *requirement, Type memberTy, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocatorBuilder locator) { + assert(baseTy); + assert(memberTy); + assert(requirement); + assert(useDC); + switch (simplifyValueWitnessConstraint( + ConstraintKind::ValueWitness, baseTy, requirement, memberTy, useDC, + functionRefKind, TMF_GenerateConstraints, locator)) { + case SolutionKind::Unsolved: + llvm_unreachable("Unsolved result when generating constraints!"); + + case SolutionKind::Solved: + case SolutionKind::Error: + break; + } + } + /// Add an explicit conversion constraint (e.g., \c 'x as T'). void addExplicitConversionConstraint(Type fromType, Type toType, bool allowFixes, @@ -3265,6 +3345,12 @@ class ConstraintSystem { ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the given value witness constraint. + SolutionKind simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *member, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the optional object constraint. SolutionKind simplifyOptionalObjectConstraint( Type first, Type second, @@ -3395,9 +3481,10 @@ class ConstraintSystem { void simplifyDisjunctionChoice(Constraint *choice); /// Apply the given function builder to the closure expression. - TypeMatchResult applyFunctionBuilder(ClosureExpr *closure, Type builderType, - ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator); + TypeMatchResult matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator); private: /// The kind of bindings that are permitted. @@ -3888,6 +3975,12 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); +private: + llvm::PointerUnion applySolutionImpl( + Solution &solution, SolutionApplicationTarget target, + Type convertType, bool discardedExpr, bool performingDiagnostics); + +public: /// Apply a given solution to the expression, producing a fully /// type-checked expression. /// @@ -3895,11 +3988,22 @@ class ConstraintSystem { /// expression should be converted, if any. /// \param discardedExpr if true, the result of the expression /// is contextually ignored. - /// \param skipClosures if true, don't descend into bodies of - /// non-single expression closures. + /// \param performingDiagnostics if true, don't descend into bodies of + /// non-single expression closures, or build curry thunks. Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, bool discardedExpr, - bool skipClosures); + Type convertType, + bool discardedExpr, + bool performingDiagnostics) { + return applySolutionImpl(solution, expr, convertType, discardedExpr, + performingDiagnostics).get(); + } + + /// Apply a given solution to the body of the given function. + BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { + return cast_or_null( + applySolutionImpl(solution, fn, Type(), false, false) + .dyn_cast()); + } /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index e85b332962953..4853943d687d6 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -225,8 +225,9 @@ class DebuggerTestingTransform : public ASTWalker { // Create the closure. auto *Params = ParameterList::createEmpty(Ctx); auto *Closure = new (Ctx) - ClosureExpr(Params, SourceLoc(), SourceLoc(), SourceLoc(), TypeLoc(), - DF.getNextDiscriminator(), getCurrentDeclContext()); + ClosureExpr(SourceRange(), nullptr, Params, SourceLoc(), SourceLoc(), + SourceLoc(), TypeLoc(), DF.getNextDiscriminator(), + getCurrentDeclContext()); Closure->setImplicit(true); // TODO: Save and return the value of $OriginalExpr. @@ -260,11 +261,11 @@ void swift::performDebuggerTestingTransform(SourceFile &SF) { // Walk over all decls in the file to find the next available closure // discriminator. DiscriminatorFinder DF; - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) D->walk(DF); // Instrument the decls with checkExpect() sanity-checks. - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { DebuggerTestingTransform Transform{D->getASTContext(), DF}; D->walk(Transform); swift::verify(D); diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index 28c4645862f19..1197a950eefc5 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -28,7 +28,8 @@ template class Added { public: Added() {} - Added(E NewContents) { Contents = NewContents; } + Added(E NewContents) : Contents(NewContents) {} + Added(const Added &rhs) : Contents(rhs.Contents) {} const Added &operator=(const Added &rhs) { Contents = rhs.Contents; return *this; diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index 16770c7656fe3..b17f5dcb3e30c 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -1088,8 +1088,8 @@ static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer, // FIXME: when we can parse and typecheck the function body partially for // code completion, AFD->getBody() check can be removed. if (Loc.isValid() && - AFD->getSourceRange().isValid() && - SM.rangeContainsTokenLoc(AFD->getSourceRange(), Loc) && + AFD->getBodySourceRange().isValid() && + SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) && AFD->getBody()) { namelookup::FindLocalVal(SM, Loc, Consumer).visit(AFD->getBody()); } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 38d0202d4e7e9..eeab6b6062f12 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1316,21 +1316,27 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { const_cast(E)->walk(walker); } -/// Look for any property references in closures that lack a "self." qualifier. -/// Within a closure, we require that the source code contain "self." explicitly -/// because 'self' is captured, not the property value. This is a common source -/// of confusion, so we force an explicit self. +/// Look for any property references in closures that lack a 'self.' qualifier. +/// Within a closure, we require that the source code contain 'self.' explicitly +/// (or that the closure explicitly capture 'self' in the capture list) because +/// 'self' is captured, not the property value. This is a common source of +/// confusion, so we force an explicit self. static void diagnoseImplicitSelfUseInClosure(const Expr *E, const DeclContext *DC) { class DiagnoseWalker : public ASTWalker { ASTContext &Ctx; - unsigned InClosure; + SmallVector Closures; public: - explicit DiagnoseWalker(ASTContext &ctx, bool isAlreadyInClosure) - : Ctx(ctx), InClosure(isAlreadyInClosure) {} + explicit DiagnoseWalker(ASTContext &ctx, AbstractClosureExpr *ACE) + : Ctx(ctx), Closures() { + if (ACE) + Closures.push_back(ACE); + } - /// Return true if this is an implicit reference to self. - static bool isImplicitSelfUse(Expr *E) { + /// Return true if this is an implicit reference to self which is required + /// to be explicit in an escaping closure. Metatype references and value + /// type references are excluded. + static bool isImplicitSelfParamUseLikelyToCauseCycle(Expr *E) { auto *DRE = dyn_cast(E); if (!DRE || !DRE->isImplicit() || !isa(DRE->getDecl()) || @@ -1345,11 +1351,19 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, return false; // Metatype self captures don't extend the lifetime of an object. - return !ty->is(); + if (ty->is()) + return false; + + // If self does not have reference semantics, it is very unlikely that + // capturing it will create a reference cycle. + if (!ty->hasReferenceSemantics()) + return false; + + return true; } - /// Return true if this is a closure expression that will require "self." - /// qualification of member references. + /// Return true if this is a closure expression that will require explicit + /// use or capture of "self." for qualification of member references. static bool isClosureRequiringSelfQualification( const AbstractClosureExpr *CE) { // If the closure's type was inferred to be noescape, then it doesn't @@ -1371,43 +1385,48 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // If this is a potentially-escaping closure expression, start looking // for references to self if we aren't already. if (isClosureRequiringSelfQualification(CE)) - ++InClosure; + Closures.push_back(CE); } // If we aren't in a closure, no diagnostics will be produced. - if (!InClosure) + if (Closures.size() == 0) return { true, E }; - // If we see a property reference with an implicit base from within a - // closure, then reject it as requiring an explicit "self." qualifier. We - // do this in explicit closures, not autoclosures, because otherwise the - // transparence of autoclosures is lost. auto &Diags = Ctx.Diags; + + // Diagnostics should correct the innermost closure + auto *ACE = Closures[Closures.size() - 1]; + assert(ACE); + + SourceLoc memberLoc = SourceLoc(); if (auto *MRE = dyn_cast(E)) - if (isImplicitSelfUse(MRE->getBase())) { + if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase())) { auto baseName = MRE->getMember().getDecl()->getBaseName(); - Diags.diagnose(MRE->getLoc(), + memberLoc = MRE->getLoc(); + Diags.diagnose(memberLoc, diag::property_use_in_closure_without_explicit_self, - baseName.getIdentifier()) - .fixItInsert(MRE->getLoc(), "self."); - return { false, E }; + baseName.getIdentifier()); } // Handle method calls with a specific diagnostic + fixit. if (auto *DSCE = dyn_cast(E)) - if (isImplicitSelfUse(DSCE->getBase()) && + if (isImplicitSelfParamUseLikelyToCauseCycle(DSCE->getBase()) && isa(DSCE->getFn())) { auto MethodExpr = cast(DSCE->getFn()); + memberLoc = DSCE->getLoc(); Diags.diagnose(DSCE->getLoc(), diag::method_call_in_closure_without_explicit_self, - MethodExpr->getDecl()->getBaseName().getIdentifier()) - .fixItInsert(DSCE->getLoc(), "self."); - return { false, E }; + MethodExpr->getDecl()->getBaseName().getIdentifier()); } + if (memberLoc.isValid()) { + emitFixIts(Diags, memberLoc, ACE); + return { false, E }; + } + // Catch any other implicit uses of self with a generic diagnostic. - if (isImplicitSelfUse(E)) + if (isImplicitSelfParamUseLikelyToCauseCycle(E)) Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure); return { true, E }; @@ -1416,26 +1435,144 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, Expr *walkToExprPost(Expr *E) override { if (auto *CE = dyn_cast(E)) { if (isClosureRequiringSelfQualification(CE)) { - assert(InClosure); - --InClosure; + assert(Closures.size() > 0); + Closures.pop_back(); } } return E; } + + /// Emit any fix-its for this error. + void emitFixIts(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const AbstractClosureExpr *ACE) { + // This error can be fixed by either capturing self explicitly (if in an + // explicit closure), or referencing self explicitly. + if (auto *CE = dyn_cast(ACE)) { + if (diagnoseAlmostMatchingCaptures(Diags, memberLoc, CE)) { + // Bail on the rest of the diagnostics. Offering the option to + // capture 'self' explicitly will result in an error, and using + // 'self.' explicitly will be accessing something other than the + // self param. + // FIXME: We could offer a special fixit in the [weak self] case to insert 'self?.'... + return; + } + emitFixItsForExplicitClosure(Diags, memberLoc, CE); + } else { + // If this wasn't an explicit closure, just offer the fix-it to + // reference self explicitly. + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + } + } + + /// Diagnose any captures which might have been an attempt to capture + /// \c self strongly, but do not actually enable implicit \c self. Returns + /// whether there were any such captures to diagnose. + bool diagnoseAlmostMatchingCaptures(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + // If we've already captured something with the name "self" other than + // the actual self param, offer special diagnostics. + if (auto *VD = closureExpr->getCapturedSelfDecl()) { + // Either this is a weak capture of self... + if (VD->getType()->is()) { + Diags.diagnose(VD->getLoc(), diag::note_self_captured_weakly); + // ...or something completely different. + } else { + Diags.diagnose(VD->getLoc(), diag::note_other_self_capture); + } + + return true; + } + return false; + } + + /// Emit fix-its for invalid use of implicit \c self in an explicit closure. + /// The error can be solved by capturing self explicitly, + /// or by using \c self. explicitly. + void emitFixItsForExplicitClosure(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + auto diag = Diags.diagnose(closureExpr->getLoc(), + diag::note_capture_self_explicitly); + // There are four different potential fix-its to offer based on the + // closure signature: + // 1. There is an existing capture list which already has some + // entries. We need to insert 'self' into the capture list along + // with a separating comma. + // 2. There is an existing capture list, but it is empty (jusr '[]'). + // We can just insert 'self'. + // 3. Arguments or types are already specified in the signature, + // but there is no existing capture list. We will need to insert + // the capture list, but 'in' will already be present. + // 4. The signature empty so far. We must insert the full capture + // list as well as 'in'. + const auto brackets = closureExpr->getBracketRange(); + if (brackets.isValid()) { + emitInsertSelfIntoCaptureListFixIt(brackets, diag); + } + else { + emitInsertNewCaptureListFixIt(closureExpr, diag); + } + } + + /// Emit a fix-it for inserting \c self into in existing capture list, along + /// with a trailing comma if needed. The fix-it will be attached to the + /// provided diagnostic \c diag. + void emitInsertSelfIntoCaptureListFixIt(SourceRange brackets, + InFlightDiagnostic &diag) { + // Look for any non-comment token. If there's anything before the + // closing bracket, we assume that it is a valid capture list entry and + // insert 'self,'. If it wasn't a valid entry, then we will at least not + // be introducing any new errors/warnings... + const auto locAfterBracket = brackets.Start.getAdvancedLoc(1); + const auto nextAfterBracket = + Lexer::getTokenAtLocation(Ctx.SourceMgr, locAfterBracket, + CommentRetentionMode::None); + if (nextAfterBracket.getLoc() != brackets.End) + diag.fixItInsertAfter(brackets.Start, "self, "); + else + diag.fixItInsertAfter(brackets.Start, "self"); + } + + /// Emit a fix-it for inserting a capture list into a closure that does not + /// already have one, along with a trailing \c in if necessary. The fix-it + /// will be attached to the provided diagnostic \c diag. + void emitInsertNewCaptureListFixIt(const ClosureExpr *closureExpr, + InFlightDiagnostic &diag) { + if (closureExpr->getInLoc().isValid()) { + diag.fixItInsertAfter(closureExpr->getLoc(), " [self]"); + return; + } + + // If there's a (non-comment) token immediately following the + // opening brace of the closure, we may need to pad the fix-it + // with a space. + const auto nextLoc = closureExpr->getLoc().getAdvancedLoc(1); + const auto next = + Lexer::getTokenAtLocation(Ctx.SourceMgr, nextLoc, + CommentRetentionMode::None); + std::string trailing = next.getLoc() == nextLoc ? " " : ""; + + diag.fixItInsertAfter(closureExpr->getLoc(), " [self] in" + trailing); + } }; - bool isAlreadyInClosure = false; + AbstractClosureExpr *ACE = nullptr; if (DC->isLocalContext()) { - while (DC->getParent()->isLocalContext() && !isAlreadyInClosure) { + while (DC->getParent()->isLocalContext() && !ACE) { if (auto *closure = dyn_cast(DC)) if (DiagnoseWalker::isClosureRequiringSelfQualification(closure)) - isAlreadyInClosure = true; + ACE = const_cast(closure); DC = DC->getParent(); } } auto &ctx = DC->getASTContext(); - const_cast(E)->walk(DiagnoseWalker(ctx, isAlreadyInClosure)); + const_cast(E)->walk(DiagnoseWalker(ctx, ACE)); } bool TypeChecker::getDefaultGenericArgumentsString( diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 4b86626e941da..1416e6ad8444b 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -61,19 +61,19 @@ namespace { /// Load a module referenced by an import statement. /// /// Returns null if no module can be loaded. - ModuleDecl *getModule(ArrayRef> ModuleID); + ModuleDecl *getModule(ArrayRef> ModuleID); }; } // end anonymous namespace ModuleDecl * -NameBinder::getModule(ArrayRef> modulePath) { +NameBinder::getModule(ArrayRef> modulePath) { assert(!modulePath.empty()); auto moduleID = modulePath[0]; // The Builtin module cannot be explicitly imported unless we're a .sil file // or in the REPL. if ((SF.Kind == SourceFileKind::SIL || SF.Kind == SourceFileKind::REPL) && - moduleID.first == Context.TheBuiltinModule->getName()) + moduleID.Item == Context.TheBuiltinModule->getName()) return Context.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -82,10 +82,10 @@ NameBinder::getModule(ArrayRef> modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for our fake overlays as well. - if (moduleID.first == SF.getParentModule()->getName() && + if (moduleID.Item == SF.getParentModule()->getName() && modulePath.size() == 1) { if (auto importer = Context.getClangModuleLoader()) - return importer->loadModule(moduleID.second, modulePath); + return importer->loadModule(moduleID.Loc, modulePath); return nullptr; } @@ -170,17 +170,17 @@ static bool shouldImportSelfImportClang(const ImportDecl *ID, void NameBinder::addImport( SmallVectorImpl &imports, ImportDecl *ID) { - if (ID->getModulePath().front().first == SF.getParentModule()->getName() && + if (ID->getModulePath().front().Item == SF.getParentModule()->getName() && ID->getModulePath().size() == 1 && !shouldImportSelfImportClang(ID, SF)) { // If the imported module name is the same as the current module, // produce a diagnostic. StringRef filename = llvm::sys::path::filename(SF.getFilename()); if (filename.empty()) Context.Diags.diagnose(ID, diag::sema_import_current_module, - ID->getModulePath().front().first); + ID->getModulePath().front().Item); else Context.Diags.diagnose(ID, diag::sema_import_current_module_with_file, - filename, ID->getModulePath().front().first); + filename, ID->getModulePath().front().Item); ID->setModule(SF.getParentModule()); return; } @@ -190,7 +190,7 @@ void NameBinder::addImport( SmallString<64> modulePathStr; interleave(ID->getModulePath(), [&](ImportDecl::AccessPathElement elem) { - modulePathStr += elem.first.str(); + modulePathStr += elem.Item.str(); }, [&] { modulePathStr += "."; }); @@ -214,7 +214,7 @@ void NameBinder::addImport( topLevelModule = M; } else { // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = ID->getModulePath().front().first; + Identifier topLevelName = ID->getModulePath().front().Item; topLevelModule = Context.getLoadedModule(topLevelName); if (!topLevelModule) { // Clang can sometimes import top-level modules as if they were @@ -234,8 +234,8 @@ void NameBinder::addImport( !topLevelModule->isTestingEnabled() && !topLevelModule->isNonSwiftModule() && Context.LangOpts.EnableTestableAttrRequiresTestableModule) { - diagnose(ID->getModulePath().front().second, diag::module_not_testable, - ID->getModulePath().front().first); + diagnose(ID->getModulePath().front().Loc, diag::module_not_testable, + ID->getModulePath().front().Item); testableAttr->setInvalid(); } @@ -243,9 +243,9 @@ void NameBinder::addImport( StringRef privateImportFileName; if (privateImportAttr) { if (!topLevelModule || !topLevelModule->arePrivateImportsEnabled()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_for_private_import, - ID->getModulePath().front().first); + ID->getModulePath().front().Item); privateImportAttr->setInvalid(); } else { privateImportFileName = privateImportAttr->getSourceFile(); @@ -256,7 +256,7 @@ void NameBinder::addImport( !topLevelModule->isResilient() && !topLevelModule->isNonSwiftModule() && !ID->getAttrs().hasAttribute()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_with_library_evolution, topLevelModule->getName(), SF.getParentModule()->getName()); } @@ -296,17 +296,17 @@ void NameBinder::addImport( // FIXME: Doesn't handle scoped testable imports correctly. assert(declPath.size() == 1 && "can't handle sub-decl imports"); SmallVector decls; - lookupInModule(topLevelModule, declPath.front().first, decls, + lookupInModule(topLevelModule, declPath.front().Item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, &SF); if (decls.empty()) { diagnose(ID, diag::decl_does_not_exist_in_module, static_cast(ID->getImportKind()), - declPath.front().first, - ID->getModulePath().front().first) - .highlight(SourceRange(declPath.front().second, - declPath.back().second)); + declPath.front().Item, + ID->getModulePath().front().Item) + .highlight(SourceRange(declPath.front().Loc, + declPath.back().Loc)); return; } @@ -316,7 +316,7 @@ void NameBinder::addImport( if (!actualKind.hasValue()) { // FIXME: print entire module name? diagnose(ID, diag::ambiguous_decl_in_module, - declPath.front().first, M->getName()); + declPath.front().Item, M->getName()); for (auto next : decls) diagnose(next, diag::found_candidate); @@ -338,7 +338,7 @@ void NameBinder::addImport( getImportKindString(ID->getImportKind()))); } else { emittedDiag.emplace(diagnose(ID, diag::imported_decl_is_wrong_kind, - declPath.front().first, + declPath.front().Item, getImportKindString(ID->getImportKind()), static_cast(*actualKind))); } @@ -402,7 +402,7 @@ void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { // Make sure we skip adding the standard library imports if the // source file is empty. - if (SF.ASTStage == SourceFile::NameBound || SF.Decls.empty()) { + if (SF.ASTStage == SourceFile::NameBound || SF.getTopLevelDecls().empty()) { SF.ASTStage = SourceFile::NameBound; return; } @@ -417,7 +417,7 @@ void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { // Do a prepass over the declarations to find and load the imported modules // and map operator decls. - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { if (auto *ID = dyn_cast(D)) { Binder.addImport(ImportedModules, ID); } else if (auto *OD = dyn_cast(D)) { diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index 9109dd250f607..839176b7aa88d 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -705,7 +705,7 @@ void swift::performPCMacro(SourceFile &SF) { }; ExpressionFinder EF; - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { D->walk(EF); } } diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index 690631dc9f5cb..c1300e38e9054 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -62,15 +62,15 @@ void SourceLoader::collectVisibleTopLevelModuleNames( // TODO: Implement? } -bool SourceLoader::canImportModule(std::pair ID) { +bool SourceLoader::canImportModule(Located ID) { // Search the memory buffers to see if we can find this file on disk. - FileOrError inputFileOrError = findModule(Ctx, ID.first.str(), - ID.second); + FileOrError inputFileOrError = findModule(Ctx, ID.Item.str(), + ID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(ID.second, diag::sema_opening_import, - ID.first, err.message()); + Ctx.Diags.diagnose(ID.Loc, diag::sema_opening_import, + ID.Item, err.message()); } return false; @@ -79,21 +79,20 @@ bool SourceLoader::canImportModule(std::pair ID) { } ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, - ArrayRef> path) { + ArrayRef> path) { // FIXME: Swift submodules? if (path.size() > 1) return nullptr; auto moduleID = path[0]; - FileOrError inputFileOrError = findModule(Ctx, moduleID.first.str(), - moduleID.second); + FileOrError inputFileOrError = findModule(Ctx, moduleID.Item.str(), + moduleID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(moduleID.second, diag::sema_opening_import, - moduleID.first, err.message()); + Ctx.Diags.diagnose(moduleID.Loc, diag::sema_opening_import, + moduleID.Item, err.message()); } return nullptr; @@ -116,10 +115,10 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, else bufferID = Ctx.SourceMgr.addNewSourceBuffer(std::move(inputFile)); - auto *importMod = ModuleDecl::create(moduleID.first, Ctx); + auto *importMod = ModuleDecl::create(moduleID.Item, Ctx); if (EnableLibraryEvolution) importMod->setResilienceStrategy(ResilienceStrategy::Resilient); - Ctx.LoadedModules[moduleID.first] = importMod; + Ctx.LoadedModules[moduleID.Item] = importMod; auto implicitImportKind = SourceFile::ImplicitModuleImportKind::Stdlib; if (!Ctx.getStdlibModule()) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 600503aa5ffe8..9a2b2db26aa25 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -119,6 +119,9 @@ class AttributeChecker : public AttributeVisitor { // TODO(TF-828): Upstream `@differentiable` attribute type-checking from // tensorflow branch. IGNORED_ATTR(Differentiable) + // TODO(TF-830): Upstream `@transpose` attribute type-checking from tensorflow + // branch. + IGNORED_ATTR(Transpose) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { @@ -250,8 +253,7 @@ class AttributeChecker : public AttributeVisitor { void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); void visitNonEphemeralAttr(NonEphemeralAttr *attr); - void checkOriginalDefinedInAttrs(ArrayRef Attrs); - + void checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs); void visitDerivativeAttr(DerivativeAttr *attr); }; } // end anonymous namespace @@ -270,7 +272,7 @@ void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) { if (!(isa(D) && D->isImplicit())) diagnoseAndRemoveAttr(attr, diag::transparent_in_classes_not_supported); } - + if (auto *VD = dyn_cast(D)) { // Stored properties and variables can't be transparent. if (VD->hasStorage()) @@ -337,7 +339,7 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) { } } } - + // Verify that we don't have a static function. if (FD->isStatic()) diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating); @@ -558,7 +560,7 @@ isAcceptableOutletType(Type type, bool &isArray, ASTContext &ctx) { if (type->isExistentialType()) return diag::iboutlet_nonobjc_protocol; - + // No other types are permitted. return diag::iboutlet_nonobject_type; } @@ -1070,7 +1072,7 @@ void TypeChecker::checkDeclAttributes(Decl *D) { else Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr); } - Checker.checkOriginalDefinedInAttrs(ODIAttrs); + Checker.checkOriginalDefinedInAttrs(D, ODIAttrs); } /// Returns true if the given method is an valid implementation of a @@ -1428,7 +1430,7 @@ void AttributeChecker::visitSwiftNativeObjCRuntimeBaseAttr( attr->setInvalid(); return; } - + if (theClass->hasSuperclass()) { diagnose(attr->getLocation(), diag::swift_native_objc_runtime_base_not_on_root_class); @@ -1580,7 +1582,7 @@ void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) { // Check the type. It must be an [unchecked]optional, weak, a normal // class, AnyObject, or classbound protocol. // It must conform to the NSCopying protocol. - + } void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, @@ -1592,7 +1594,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, UIApplicationMainClass, NSApplicationMainClass, }; - + unsigned applicationMainKind; if (isa(attr)) applicationMainKind = UIApplicationMainClass; @@ -1600,9 +1602,9 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, applicationMainKind = NSApplicationMainClass; else llvm_unreachable("not an ApplicationMain attr"); - + auto *CD = dyn_cast(D); - + // The applicant not being a class should have been diagnosed by the early // checker. if (!CD) return; @@ -1615,7 +1617,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, attr->setInvalid(); return; } - + // @XXApplicationMain classes must conform to the XXApplicationDelegate // protocol. auto *SF = cast(CD->getModuleScopeContext()); @@ -1644,7 +1646,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, if (attr->isInvalid()) return; - + // Register the class as the main class in the module. If there are multiples // they will be diagnosed. if (SF->registerMainClass(CD, attr->getLocation())) @@ -1967,7 +1969,7 @@ void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { if (isa(D)) { diagnose(attr->getLocation(), diag::fixed_layout_struct) .fixItReplace(attr->getRange(), "@frozen"); - } + } auto *VD = cast(D); @@ -2467,10 +2469,10 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { attr->setInvalid(); return; } - + return; } - + // If the nominal type is a function builder type, verify that D is a // function, storage with an explicit getter, or parameter of function type. if (nominal->getAttrs().hasAttribute()) { @@ -2632,21 +2634,48 @@ void TypeChecker::checkParameterAttributes(ParameterList *params) { } } -void -AttributeChecker::checkOriginalDefinedInAttrs( +void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs) { - llvm::SmallSet AllPlatforms; + if (Attrs.empty()) + return; + auto &Ctx = D->getASTContext(); + OriginallyDefinedInAttr* theAttr = nullptr; // Attrs are in the reverse order of the source order. We need to visit them // in source order to diagnose the later attribute. - for (auto It = Attrs.rbegin(), End = Attrs.rend(); It != End; ++ It) { - auto *Attr = *It; - auto CurPlat = Attr->Platform; - if (!AllPlatforms.insert(CurPlat).second) { + for (auto *Attr: Attrs) { + if (!Attr->isActivePlatform(Ctx)) + continue; + if (theAttr) { // Only one version number is allowed for one platform name. - diagnose(Attr->AtLoc, diag::originally_defined_in_dupe_platform, + diagnose(theAttr->AtLoc, diag::originally_defined_in_dupe_platform, platformString(Attr->Platform)); + return; + } else { + theAttr = Attr; } } + if (!theAttr) + return; + assert(theAttr); + static StringRef AttrName = "_originallyDefinedIn"; + auto AtLoc = theAttr->AtLoc; + if (!D->getDeclContext()->isModuleScopeContext()) { + diagnose(AtLoc, diag::originally_definedin_topleve_decl, AttrName); + return; + } + auto AvailRange = AvailabilityInference::availableRange(D, Ctx); + if (!AvailRange.getOSVersion().hasLowerEndpoint()) { + diagnose(AtLoc, diag::originally_definedin_need_available, + AttrName); + return; + } + auto AvailBegin = AvailRange.getOSVersion().getLowerEndpoint(); + if (AvailBegin >= theAttr->MovedVersion) { + diagnose(AtLoc, + diag::originally_definedin_must_after_available_version, + AttrName); + return; + } } Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, @@ -2913,7 +2942,7 @@ static bool conformsToDifferentiable(Type type, DeclContext *DC) { return !tanType.isNull() && !tanType->hasError(); }; -IndexSubset *TypeChecker::inferDifferentiationParameters( +IndexSubset *TypeChecker::inferDifferentiabilityParameters( AbstractFunctionDecl *AFD, GenericEnvironment *derivativeGenEnv) { auto &ctx = AFD->getASTContext(); auto *functionType = AFD->getInterfaceType()->castTo(); @@ -2952,7 +2981,7 @@ IndexSubset *TypeChecker::inferDifferentiationParameters( for (auto ¶m : functionType->getParams()) allParamTypes.push_back(param.getPlainType()); - // Set differentiation parameters. + // Set differentiability parameters. for (unsigned i : range(parameterBits.size())) if (isDifferentiableParam(i)) parameterBits.set(i); @@ -2960,14 +2989,15 @@ IndexSubset *TypeChecker::inferDifferentiationParameters( return IndexSubset::get(ctx, parameterBits); } -// Computes `IndexSubset` from the given parsed differentiation parameters -// (possibly empty) for the given function and derivative generic environment, -// then verifies that the parameter indices are valid. +// Computes the differentiability parameter indices from the given parsed +// differentiability parameters for the given original or derivative +// `AbstractFunctionDecl` and derivative generic environment. On error, emits +// diagnostics and returns `nullptr`. // - If parsed parameters are empty, infer parameter indices. // - Otherwise, build parameter indices from parsed parameters. // The attribute name/location are used in diagnostics. -static IndexSubset *computeDifferentiationParameters( - ArrayRef parsedWrtParams, +static IndexSubset *computeDifferentiabilityParameters( + ArrayRef parsedDiffParams, AbstractFunctionDecl *function, GenericEnvironment *derivativeGenEnv, StringRef attrName, SourceLoc attrLoc) { auto &ctx = function->getASTContext(); @@ -3007,13 +3037,14 @@ static IndexSubset *computeDifferentiationParameters( } } - // If parsed differentiation parameters are empty, infer parameter indices + // If parsed differentiability parameters are empty, infer parameter indices // from the function type. - if (parsedWrtParams.empty()) - return TypeChecker::inferDifferentiationParameters(function, - derivativeGenEnv); + if (parsedDiffParams.empty()) + return TypeChecker::inferDifferentiabilityParameters(function, + derivativeGenEnv); - // Otherwise, build parameter indices from parsed differentiation parameters. + // Otherwise, build parameter indices from parsed differentiability + // parameters. auto numUncurriedParams = functionType->getNumParams(); if (auto *resultFnType = functionType->getResult()->getAs()) { @@ -3021,17 +3052,17 @@ static IndexSubset *computeDifferentiationParameters( } llvm::SmallBitVector parameterBits(numUncurriedParams); int lastIndex = -1; - for (unsigned i : indices(parsedWrtParams)) { - auto paramLoc = parsedWrtParams[i].getLoc(); - switch (parsedWrtParams[i].getKind()) { + for (unsigned i : indices(parsedDiffParams)) { + auto paramLoc = parsedDiffParams[i].getLoc(); + switch (parsedDiffParams[i].getKind()) { case ParsedAutoDiffParameter::Kind::Named: { auto nameIter = llvm::find_if(params.getArray(), [&](ParamDecl *param) { - return param->getName() == parsedWrtParams[i].getName(); + return param->getName() == parsedDiffParams[i].getName(); }); // Parameter name must exist. if (nameIter == params.end()) { diags.diagnose(paramLoc, diag::diff_params_clause_param_name_unknown, - parsedWrtParams[i].getName()); + parsedDiffParams[i].getName()); return nullptr; } // Parameter names must be specified in the original order. @@ -3061,7 +3092,7 @@ static IndexSubset *computeDifferentiationParameters( break; } case ParsedAutoDiffParameter::Kind::Ordered: { - auto index = parsedWrtParams[i].getIndex(); + auto index = parsedDiffParams[i].getIndex(); if (index >= numParams) { diags.diagnose(paramLoc, diag::diff_params_clause_param_index_out_of_range); @@ -3082,50 +3113,53 @@ static IndexSubset *computeDifferentiationParameters( return IndexSubset::get(ctx, parameterBits); } -// Checks if the given `IndexSubset` instance is valid for the given function +// Checks if the given differentiability parameter indices are valid for the +// given original or derivative `AbstractFunctionDecl` and original function // type in the given derivative generic environment and module context. Returns // true on error. -// The parsed differentiation parameters and attribute location are used in +// +// The parsed differentiability parameters and attribute location are used in // diagnostics. -static bool checkDifferentiationParameters( - AbstractFunctionDecl *AFD, IndexSubset *indices, +static bool checkDifferentiabilityParameters( + AbstractFunctionDecl *AFD, IndexSubset *diffParamIndices, AnyFunctionType *functionType, GenericEnvironment *derivativeGenEnv, - ModuleDecl *module, ArrayRef parsedWrtParams, + ModuleDecl *module, ArrayRef parsedDiffParams, SourceLoc attrLoc) { auto &ctx = AFD->getASTContext(); auto &diags = ctx.Diags; - // Diagnose empty parameter indices. This occurs when no `wrt:` clause is - // declared and no differentiation parameters can be inferred. - if (indices->isEmpty()) { + // Diagnose empty differentiability indices. No differentiability parameters + // were resolved or inferred. + if (diffParamIndices->isEmpty()) { diags.diagnose(attrLoc, diag::diff_params_clause_no_inferred_parameters); return true; } - // Check that differentiation parameters have allowed types. - SmallVector wrtParamTypes; - autodiff::getSubsetParameterTypes(indices, functionType, wrtParamTypes); - for (unsigned i : range(wrtParamTypes.size())) { + // Check that differentiability parameters have allowed types. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(diffParamIndices, functionType, + diffParamTypes); + for (unsigned i : range(diffParamTypes.size())) { SourceLoc loc = - parsedWrtParams.empty() ? attrLoc : parsedWrtParams[i].getLoc(); - auto wrtParamType = wrtParamTypes[i]; + parsedDiffParams.empty() ? attrLoc : parsedDiffParams[i].getLoc(); + auto diffParamType = diffParamTypes[i]; // `inout` parameters are not yet supported. - if (wrtParamType->is()) { + if (diffParamType->is()) { diags.diagnose(loc, diag::diff_params_clause_cannot_diff_wrt_inout_parameter, - wrtParamType); + diffParamType); return true; } - if (!wrtParamType->hasTypeParameter()) - wrtParamType = wrtParamType->mapTypeOutOfContext(); + if (!diffParamType->hasTypeParameter()) + diffParamType = diffParamType->mapTypeOutOfContext(); if (derivativeGenEnv) - wrtParamType = derivativeGenEnv->mapTypeIntoContext(wrtParamType); + diffParamType = derivativeGenEnv->mapTypeIntoContext(diffParamType); else - wrtParamType = AFD->mapTypeIntoContext(wrtParamType); + diffParamType = AFD->mapTypeIntoContext(diffParamType); // Parameter must conform to `Differentiable`. - if (!conformsToDifferentiable(wrtParamType, AFD)) { + if (!conformsToDifferentiable(diffParamType, AFD)) { diags.diagnose(loc, diag::diff_params_clause_param_not_differentiable, - wrtParamType); + diffParamType); return true; } } @@ -3153,20 +3187,15 @@ static AbstractFunctionDecl *findAbstractFunctionDecl( // Perform lookup. LookupResult results; + // If `baseType` is not null but `lookupContext` is a type context, set + // `baseType` to the `self` type of `lookupContext` to perform member lookup. + if (!baseType && lookupContext->isTypeContext()) + baseType = lookupContext->getSelfTypeInContext(); if (baseType) { results = TypeChecker::lookupMember(lookupContext, baseType, funcName); } else { results = TypeChecker::lookupUnqualified(lookupContext, funcName, funcNameLoc, lookupOptions); - - // If looking up an operator within a type context, look specifically within - // the type context. - // This tries to resolve unqualified operators, like `+`. - if (funcName.isOperator() && lookupContext->isTypeContext()) { - if (auto tmp = TypeChecker::lookupMember( - lookupContext, lookupContext->getSelfTypeInContext(), funcName)) - results = tmp; - } } // Initialize error flags. @@ -3263,25 +3292,34 @@ static bool checkFunctionSignature( return false; } + // Map type into the required function type's generic signature, if it exists. + // This is significant when the required generic signature has same-type + // requirements while the candidate generic signature does not. + auto mapType = [&](Type type) { + if (!requiredGenSig) + return type->getCanonicalType(); + return requiredGenSig->getCanonicalTypeInContext(type); + }; + // Check that parameter types match, disregarding labels. if (required->getNumParams() != candidateFnTy->getNumParams()) return false; if (!std::equal(required->getParams().begin(), required->getParams().end(), candidateFnTy->getParams().begin(), - [](AnyFunctionType::Param x, AnyFunctionType::Param y) { - return x.getPlainType()->isEqual(y.getPlainType()); + [&](AnyFunctionType::Param x, AnyFunctionType::Param y) { + return x.getPlainType()->isEqual(mapType(y.getPlainType())); })) return false; // If required result type is not a function type, check that result types // match exactly. auto requiredResultFnTy = dyn_cast(required.getResult()); + auto candidateResultTy = mapType(candidateFnTy.getResult()); if (!requiredResultFnTy) { auto requiredResultTupleTy = dyn_cast(required.getResult()); - auto candidateResultTupleTy = - dyn_cast(candidateFnTy.getResult()); + auto candidateResultTupleTy = dyn_cast(candidateResultTy); if (!requiredResultTupleTy || !candidateResultTupleTy) - return required.getResult()->isEqual(candidateFnTy.getResult()); + return required.getResult()->isEqual(candidateResultTy); // If result types are tuple types, check that element types match, // ignoring labels. if (requiredResultTupleTy->getNumElements() != @@ -3294,29 +3332,23 @@ static bool checkFunctionSignature( } // Required result type is a function. Recurse. - return checkFunctionSignature(requiredResultFnTy, candidateFnTy.getResult()); + return checkFunctionSignature(requiredResultFnTy, candidateResultTy); }; -// Returns an `AnyFunctionType` with the same `ExtInfo` as `fnType`, but with -// the given parameters, result type, and generic signature. If the given -// generic signature is `null`, use the generic signature of `fnType`. +// Returns an `AnyFunctionType` from the given parameters, result type, and +// generic signature. static AnyFunctionType * -makeFunctionType(AnyFunctionType *fnType, - ArrayRef parameters, Type resultType, +makeFunctionType(ArrayRef parameters, Type resultType, GenericSignature genericSignature) { - if (!genericSignature) - if (auto *genericFunctionType = fnType->getAs()) - genericSignature = genericFunctionType->getGenericSignature(); if (genericSignature) - return GenericFunctionType::get(genericSignature, parameters, resultType, - fnType->getExtInfo()); - return FunctionType::get(parameters, resultType, fnType->getExtInfo()); + return GenericFunctionType::get(genericSignature, parameters, resultType); + return FunctionType::get(parameters, resultType); } // Computes the original function type corresponding to the given derivative -// function type. -AnyFunctionType * -getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { +// function type. Used for `@derivative` attribute type-checking. +static AnyFunctionType * +getDerivativeOriginalFunctionType(AnyFunctionType *derivativeFnTy) { // Unwrap curry levels. At most, two parameter lists are necessary, for // curried method types with a `(Self)` parameter list. SmallVector curryLevels; @@ -3334,7 +3366,7 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { "Expected derivative result to be a two-element tuple"); auto originalResult = derivativeResult->getElement(0).getType(); auto *originalType = makeFunctionType( - curryLevels.back(), curryLevels.back()->getParams(), originalResult, + curryLevels.back()->getParams(), originalResult, curryLevels.size() == 1 ? derivativeFnTy->getOptGenericSignature() : nullptr); @@ -3345,7 +3377,7 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { unsigned i = pair.index(); AnyFunctionType *curryLevel = pair.value(); originalType = - makeFunctionType(curryLevel, curryLevel->getParams(), originalType, + makeFunctionType(curryLevel->getParams(), originalType, i == curryLevelsWithoutLast.size() - 1 ? derivativeFnTy->getOptGenericSignature() : nullptr); @@ -3353,14 +3385,28 @@ getAutoDiffOriginalFunctionType(AnyFunctionType *derivativeFnTy) { return originalType; } -void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { +/// Typechecks the given derivative attribute `attr` on decl `D`. +/// +/// Effects are: +/// - Sets the original function and parameter indices on `attr`. +/// - Diagnoses errors. +/// - Stores the attribute in `ASTContext::DerivativeAttrs`. +/// +/// \returns true on error, false on success. +static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, + DerivativeAttr *attr) { + // Note: Implementation must be idempotent because it can get called multiple + // times for the same attribute. + + auto &diags = Ctx.Diags; + // `@derivative` attribute requires experimental differentiable programming // to be enabled. auto &ctx = D->getASTContext(); if (!ctx.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnoseAndRemoveAttr( - attr, diag::experimental_differentiable_programming_disabled); - return; + diags.diagnose(attr->getLocation(), + diag::experimental_differentiable_programming_disabled); + return true; } auto *derivative = cast(D); auto lookupConformance = @@ -3380,26 +3426,27 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto derivativeResultTupleType = derivativeResultType->getAs(); if (!derivativeResultTupleType || derivativeResultTupleType->getNumElements() != 2) { - diagnoseAndRemoveAttr(attr, diag::derivative_attr_expected_result_tuple); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_expected_result_tuple); + return true; } auto valueResultElt = derivativeResultTupleType->getElement(0); auto funcResultElt = derivativeResultTupleType->getElement(1); // Get derivative kind and derivative function identifier. AutoDiffDerivativeFunctionKind kind; if (valueResultElt.getName().str() != "value") { - diagnoseAndRemoveAttr( - attr, diag::derivative_attr_invalid_result_tuple_value_label); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_value_label); + return true; } if (funcResultElt.getName().str() == "differential") { kind = AutoDiffDerivativeFunctionKind::JVP; } else if (funcResultElt.getName().str() == "pullback") { kind = AutoDiffDerivativeFunctionKind::VJP; } else { - diagnoseAndRemoveAttr( - attr, diag::derivative_attr_invalid_result_tuple_func_label); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_func_label); + return true; } attr->setDerivativeKind(kind); // `value: R` result tuple element must conform to `Differentiable`. @@ -3410,15 +3457,15 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto valueResultConf = TypeChecker::conformsToProtocol( valueResultType, diffableProto, derivative->getDeclContext(), None); if (!valueResultConf) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_result_value_not_differentiable, - valueResultElt.getType()); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_value_not_differentiable, + valueResultElt.getType()); + return true; } // Compute expected original function type and look up original function. auto *originalFnType = - getAutoDiffOriginalFunctionType(derivativeInterfaceType); + getDerivativeOriginalFunctionType(derivativeInterfaceType); // Returns true if the generic parameters in `source` satisfy the generic // requirements in `target`. @@ -3450,6 +3497,9 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { }; auto isValidOriginal = [&](AbstractFunctionDecl *originalCandidate) { + // TODO(TF-982): Allow derivatives on protocol requirements. + if (isa(originalCandidate->getDeclContext())) + return false; return checkFunctionSignature( cast(originalFnType->getCanonicalType()), originalCandidate->getInterfaceType()->getCanonicalType(), @@ -3457,22 +3507,23 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { }; auto noneValidDiagnostic = [&]() { - diagnose(originalName.Loc, - diag::autodiff_attr_original_decl_none_valid_found, - originalName.Name, originalFnType); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_none_valid_found, + originalName.Name, originalFnType); }; auto ambiguousDiagnostic = [&]() { - diagnose(originalName.Loc, diag::attr_ambiguous_reference_to_decl, - originalName.Name, attr->getAttrName()); + diags.diagnose(originalName.Loc, diag::attr_ambiguous_reference_to_decl, + originalName.Name, attr->getAttrName()); }; auto notFunctionDiagnostic = [&]() { - diagnose(originalName.Loc, diag::autodiff_attr_original_decl_invalid_kind, - originalName.Name); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_invalid_kind, + originalName.Name); }; std::function invalidTypeContextDiagnostic = [&]() { - diagnose(originalName.Loc, - diag::autodiff_attr_original_decl_not_same_type_context, - originalName.Name); + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_not_same_type_context, + originalName.Name); }; // Returns true if the derivative function and original function candidate are @@ -3493,8 +3544,18 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { return derivative->getParent() == func->getParent(); }; - auto lookupOptions = - defaultMemberLookupOptions | NameLookupFlags::IgnoreAccessControl; + auto resolution = TypeResolution::forContextual(derivative->getDeclContext()); + Type baseType; + if (auto *baseTypeRepr = attr->getBaseTypeRepr()) { + TypeResolutionOptions options = None; + options |= TypeResolutionFlags::AllowModule; + baseType = resolution.resolveType(baseTypeRepr, options); + } + if (baseType && baseType->hasError()) + return true; + auto lookupOptions = attr->getBaseTypeRepr() + ? defaultMemberLookupOptions + : defaultUnqualifiedLookupOptions; auto derivativeTypeCtx = derivative->getInnermostTypeContext(); if (!derivativeTypeCtx) derivativeTypeCtx = derivative->getParent(); @@ -3502,67 +3563,61 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { // Look up original function. auto *originalAFD = findAbstractFunctionDecl( - originalName.Name, originalName.Loc.getBaseNameLoc(), /*baseType*/ Type(), + originalName.Name, originalName.Loc.getBaseNameLoc(), baseType, derivativeTypeCtx, isValidOriginal, noneValidDiagnostic, ambiguousDiagnostic, notFunctionDiagnostic, lookupOptions, hasValidTypeContext, invalidTypeContextDiagnostic); - if (!originalAFD) { - attr->setInvalid(); - return; - } + if (!originalAFD) + return true; // Diagnose original stored properties. Stored properties cannot have custom // registered derivatives. if (auto *accessorDecl = dyn_cast(originalAFD)) { auto *asd = accessorDecl->getStorage(); if (asd->hasStorage()) { - diagnose(originalName.Loc, - diag::derivative_attr_original_stored_property_unsupported, - originalName.Name); - diagnose(originalAFD->getLoc(), diag::decl_declared_here, - asd->getFullName()); - attr->setInvalid(); - return; + diags.diagnose(originalName.Loc, + diag::derivative_attr_original_stored_property_unsupported, + originalName.Name); + diags.diagnose(originalAFD->getLoc(), diag::decl_declared_here, + asd->getFullName()); + return true; } } attr->setOriginalFunction(originalAFD); - // Get the resolved `wrt:` parameter indices. - auto *resolvedWrtParamIndices = attr->getParameterIndices(); + // Get the resolved differentiability parameter indices. + auto *resolvedDiffParamIndices = attr->getParameterIndices(); - // Get the parsed `wrt:` parameter indices, which have not yet been resolved. - // Parsed `wrt:` paraeter indices are defined only for parsed attributes. - auto parsedWrtParams = attr->getParsedParameters(); + // Get the parsed differentiability parameter indices, which have not yet been + // resolved. Parsed differentiability parameter indices are defined only for + // parsed attributes. + auto parsedDiffParams = attr->getParsedParameters(); - // If parameter indices are not resolved, compute them. - if (!resolvedWrtParamIndices) - resolvedWrtParamIndices = computeDifferentiationParameters( - parsedWrtParams, derivative, derivative->getGenericEnvironment(), + // If differentiability parameter indices are not resolved, compute them. + if (!resolvedDiffParamIndices) + resolvedDiffParamIndices = computeDifferentiabilityParameters( + parsedDiffParams, derivative, derivative->getGenericEnvironment(), attr->getAttrName(), attr->getLocation()); - if (!resolvedWrtParamIndices) { - attr->setInvalid(); - return; - } + if (!resolvedDiffParamIndices) + return true; - // Check if the `wrt:` parameter indices are valid. - if (checkDifferentiationParameters( - originalAFD, resolvedWrtParamIndices, originalFnType, + // Check if the differentiability parameter indices are valid. + if (checkDifferentiabilityParameters( + originalAFD, resolvedDiffParamIndices, originalFnType, derivative->getGenericEnvironment(), derivative->getModuleContext(), - parsedWrtParams, attr->getLocation())) { - attr->setInvalid(); - return; - } + parsedDiffParams, attr->getLocation())) + return true; - // Set the resolved `wrt:` parameter indices in the attribute. - attr->setParameterIndices(resolvedWrtParamIndices); + // Set the resolved differentiability parameter indices in the attribute. + attr->setParameterIndices(resolvedDiffParamIndices); - // Gather `wrt:` parameters. - SmallVector wrtParamTypes; - autodiff::getSubsetParameterTypes(resolvedWrtParamIndices, originalFnType, - wrtParamTypes); + // Gather differentiability parameters. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(resolvedDiffParamIndices, originalFnType, + diffParamTypes); - // Get the `TangentVector` associated types of the `wrt:` parameters. - auto wrtParamTanTypes = - map>(wrtParamTypes, [&](Type paramType) { + // Get the differentiability parameters' `TangentVector` associated types. + auto diffParamTanTypes = + map>(diffParamTypes, [&](Type paramType) { if (paramType->hasTypeParameter()) paramType = derivative->mapTypeIntoContext(paramType); auto conf = TypeChecker::conformsToProtocol(paramType, diffableProto, @@ -3578,61 +3633,87 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { auto resultTanType = valueResultConf.getTypeWitnessByName( valueResultType, Ctx.Id_TangentVector); + // Compute the actual differential/pullback type that we use for comparison + // with the expected type. We must canonicalize the derivative interface type + // before extracting the differential/pullback type from it, so that the + // derivative interface type generic signature is available for simplifying + // types. + CanType canActualResultType = derivativeInterfaceType->getCanonicalType(); + while (isa(canActualResultType)) { + canActualResultType = + cast(canActualResultType).getResult(); + } + CanType actualFuncEltType = + cast(canActualResultType).getElementType(1); + // Compute expected differential/pullback type. - auto funcEltType = funcResultElt.getType(); Type expectedFuncEltType; if (kind == AutoDiffDerivativeFunctionKind::JVP) { auto diffParams = map>( - wrtParamTanTypes, [&](TupleTypeElt elt) { + diffParamTanTypes, [&](TupleTypeElt elt) { return AnyFunctionType::Param(elt.getType()); }); expectedFuncEltType = FunctionType::get(diffParams, resultTanType); } else { expectedFuncEltType = FunctionType::get({AnyFunctionType::Param(resultTanType)}, - TupleType::get(wrtParamTanTypes, Ctx)); + TupleType::get(diffParamTanTypes, Ctx)); } expectedFuncEltType = expectedFuncEltType->mapTypeOutOfContext(); // Check if differential/pullback type matches expected type. - if (!funcEltType->isEqual(expectedFuncEltType)) { + if (!actualFuncEltType->isEqual(expectedFuncEltType)) { // Emit differential/pullback type mismatch error on attribute. - diagnoseAndRemoveAttr(attr, diag::derivative_attr_result_func_type_mismatch, - funcResultElt.getName(), originalAFD->getFullName()); + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_func_type_mismatch, + funcResultElt.getName(), originalAFD->getFullName()); // Emit note with expected differential/pullback type on actual type // location. auto *tupleReturnTypeRepr = cast(derivative->getBodyResultTypeLoc().getTypeRepr()); auto *funcEltTypeRepr = tupleReturnTypeRepr->getElementType(1); - diagnose(funcEltTypeRepr->getStartLoc(), - diag::derivative_attr_result_func_type_mismatch_note, - funcResultElt.getName(), expectedFuncEltType) + diags + .diagnose(funcEltTypeRepr->getStartLoc(), + diag::derivative_attr_result_func_type_mismatch_note, + funcResultElt.getName(), expectedFuncEltType) .highlight(funcEltTypeRepr->getSourceRange()); // Emit note showing original function location, if possible. if (originalAFD->getLoc().isValid()) - diagnose(originalAFD->getLoc(), - diag::derivative_attr_result_func_original_note, - originalAFD->getFullName()); - return; + diags.diagnose(originalAFD->getLoc(), + diag::derivative_attr_result_func_original_note, + originalAFD->getFullName()); + return true; } // Reject different-file derivative registration. - // TODO(TF-1021): Lift this restriction. + // TODO(TF-1021): Lift same-file derivative registration restriction. if (originalAFD->getParentSourceFile() != derivative->getParentSourceFile()) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_not_in_same_file_as_original); - return; + diags.diagnose(attr->getLocation(), + diag::derivative_attr_not_in_same_file_as_original); + return true; } // Reject duplicate `@derivative` attributes. - auto insertion = Ctx.DerivativeAttrs.try_emplace( - std::make_tuple(originalAFD, resolvedWrtParamIndices, kind), attr); - if (!insertion.second) { - diagnoseAndRemoveAttr(attr, - diag::derivative_attr_original_already_has_derivative, - originalAFD->getFullName()); - diagnose(insertion.first->getSecond()->getLocation(), - diag::derivative_attr_duplicate_note); - return; + auto &derivativeAttrs = Ctx.DerivativeAttrs[std::make_tuple( + originalAFD, resolvedDiffParamIndices, kind)]; + derivativeAttrs.insert(attr); + if (derivativeAttrs.size() > 1) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_original_already_has_derivative, + originalAFD->getFullName()); + for (auto *duplicateAttr : derivativeAttrs) { + if (duplicateAttr == attr) + continue; + diags.diagnose(duplicateAttr->getLocation(), + diag::derivative_attr_duplicate_note); + } + return true; } + + return false; +} + +void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { + if (typeCheckDerivativeAttr(Ctx, D, attr)) + attr->setInvalid(); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 72f3abe58b2ba..0f6812903c459 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -604,7 +604,7 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF, // Build refinement contexts, if necessary, for all declarations starting // with StartElem. TypeRefinementContextBuilder Builder(RootTRC, Context); - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { Builder.build(D); } } @@ -904,8 +904,9 @@ static const Decl *findContainingDeclaration(SourceRange ReferenceRange, if (!SF) return nullptr; - auto BestTopLevelDecl = llvm::find_if(SF->Decls, ContainsReferenceRange); - if (BestTopLevelDecl != SF->Decls.end()) + auto BestTopLevelDecl = llvm::find_if(SF->getTopLevelDecls(), + ContainsReferenceRange); + if (BestTopLevelDecl != SF->getTopLevelDecls().end()) return *BestTopLevelDecl; return nullptr; diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 2bdd3ec0ac52b..d9173b384bde1 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -25,6 +25,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallPtrSet.h" @@ -219,10 +220,14 @@ class FindCapturedVars : public ASTWalker { if (D->getBaseName() == Context.Id_dollarInterpolation) return { false, DRE }; + // DC is the DeclContext where D was defined + // CurDC is the DeclContext where D was referenced + auto DC = D->getDeclContext(); + // Capture the generic parameters of the decl, unless it's a // local declaration in which case we will pick up generic // parameter references transitively. - if (!D->getDeclContext()->isLocalContext()) { + if (!DC->isLocalContext()) { if (!ObjC || !D->isObjC() || isa(D)) { if (auto subMap = DRE->getDeclRef().getSubstitutions()) { for (auto type : subMap.getReplacementTypes()) { @@ -232,40 +237,57 @@ class FindCapturedVars : public ASTWalker { } } - // DC is the DeclContext where D was defined - // CurDC is the DeclContext where D was referenced - auto DC = D->getDeclContext(); + // Don't "capture" type definitions at all. + if (isa(D)) + return { false, DRE }; // A local reference is not a capture. - if (CurDC == DC) + if (CurDC == DC || isa(CurDC)) return { false, DRE }; auto TmpDC = CurDC; - - if (!isa(DC)) { - while (TmpDC != nullptr) { - if (TmpDC == DC) - break; - - // The initializer of a lazy property will eventually get - // recontextualized into it, so treat it as if it's already there. - if (auto init = dyn_cast(TmpDC)) { - if (auto lazyVar = init->getInitializedLazyVar()) { - // If we have a getter with a body, we're already re-parented - // everything so pretend we're inside the getter. - if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { - if (getter->getBody(/*canSynthesize=*/false)) { - TmpDC = getter; - continue; - } + while (TmpDC != nullptr) { + // Variables defined inside TopLevelCodeDecls are semantically + // local variables. If the reference is not from the top level, + // we have a capture. + if (isa(DC) && + (isa(TmpDC) || isa(TmpDC))) + break; + + if (TmpDC == DC) + break; + + // The initializer of a lazy property will eventually get + // recontextualized into it, so treat it as if it's already there. + if (auto init = dyn_cast(TmpDC)) { + if (auto lazyVar = init->getInitializedLazyVar()) { + // If we have a getter with a body, we're already re-parented + // everything so pretend we're inside the getter. + if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { + if (getter->getBody(/*canSynthesize=*/false)) { + TmpDC = getter; + continue; } } } + } - // We have an intervening nominal type context that is not the - // declaration context, and the declaration context is not global. - // This is not supported since nominal types cannot capture values. - if (auto NTD = dyn_cast(TmpDC)) { + // We have an intervening nominal type context that is not the + // declaration context, and the declaration context is not global. + // This is not supported since nominal types cannot capture values. + if (auto NTD = dyn_cast(TmpDC)) { + // Allow references to local functions from inside methods of a + // local type, because if the local function has captures, we'll + // diagnose them in SILGen. It's a bit unfortunate that we can't + // ban this outright, but people rely on code like this working: + // + // do { + // func local() {} + // class C { + // func method() { local() } + // } + // } + if (!isa(D)) { if (DC->isLocalContext()) { Context.Diags.diagnose(DRE->getLoc(), diag::capture_across_type_decl, NTD->getDescriptiveKind(), @@ -278,23 +300,19 @@ class FindCapturedVars : public ASTWalker { return { false, DRE }; } } - - TmpDC = TmpDC->getParent(); } - // We walked all the way up to the root without finding the declaration, - // so this is not a capture. - if (TmpDC == nullptr) - return { false, DRE }; + TmpDC = TmpDC->getParent(); } - // Don't "capture" type definitions at all. - if (isa(D)) + // We walked all the way up to the root without finding the declaration, + // so this is not a capture. + if (TmpDC == nullptr) return { false, DRE }; // Only capture var decls at global scope. Other things can be captured // if they are local. - if (!isa(D) && !DC->isLocalContext()) + if (!isa(D) && !D->isLocalCapture()) return { false, DRE }; // We're going to capture this, compute flags for the capture. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index e2d10d5fade33..ec961d18bf808 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -83,6 +83,14 @@ TypeVariableType::Implementation::getGenericParameter() const { return locator ? locator->getGenericParameter() : nullptr; } +bool TypeVariableType::Implementation::isClosureResultType() const { + if (!(locator && locator->getAnchor())) + return false; + + return isa(locator->getAnchor()) && + locator->isLastElement(); +} + void *operator new(size_t bytes, ConstraintSystem& cs, size_t alignment) { return cs.getAllocator().Allocate(bytes, alignment); @@ -1221,13 +1229,6 @@ namespace { DC = ce->getParent(); } - // Strip off any AutoClosures that were produced by a previous type check - // so that we don't choke in CSGen. - // FIXME: we shouldn't double typecheck, but it looks like code completion - // may do so in some circumstances. rdar://21466394 - if (auto autoClosure = dyn_cast(expr)) - return autoClosure->getSingleExpressionBody(); - // A 'self.init' or 'super.init' application inside a constructor will // evaluate to void, with the initializer's result implicitly rebound // to 'self'. Recognize the unresolved constructor expression and @@ -2258,7 +2259,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, result = cs.applySolution( solution, result, convertType.getType(), options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SkipMultiStmtClosures)); + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)); if (!result) { listener.applySolutionFailed(solution, expr); @@ -2565,7 +2566,8 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, - DeclContext *DC) { + DeclContext *DC, + Type patternType) { /// Type checking listener for pattern binding initializers. class BindingListener : public ExprTypeCheckListener { @@ -2762,8 +2764,10 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // if there's an error we can use that information to inform diagnostics. contextualPurpose = CTP_Initialization; - if (pattern->hasType()) { - contextualType = TypeLoc::withoutLoc(pattern->getType()); + if (isa(pattern)) { + flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; + } else if (patternType && !patternType->isEqual(Context.TheUnresolvedType)) { + contextualType = TypeLoc::withoutLoc(patternType); // If we already had an error, don't repeat the problem. if (contextualType.getType()->hasError()) @@ -2771,7 +2775,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Allow the initializer expression to establish the underlying type of an // opaque type. - if (auto opaqueType = pattern->getType()->getAs()){ + if (auto opaqueType = patternType->getAs()){ flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; flags -= TypeCheckExprFlags::ConvertTypeIsOnlyAHint; } @@ -2779,11 +2783,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Only provide a TypeLoc if it makes sense to allow diagnostics. if (auto *typedPattern = dyn_cast(pattern)) { const Pattern *inner = typedPattern->getSemanticsProvidingPattern(); - if (isa(inner) || isa(inner)) + if (isa(inner) || isa(inner)) { contextualType = typedPattern->getTypeLoc(); + if (!contextualType.getType()) + contextualType.setType(patternType); + } } - } else if (isa(pattern)) { - flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; } // Type-check the initializer. @@ -2805,8 +2810,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return true; // Apply the solution to the pattern as well. - if (coercePatternToType(pattern, TypeResolution::forContextual(DC), initTy, - options, TypeLoc())) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + if (auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, initTy, options)) { + pattern = coercedPattern; + } else { return true; } } @@ -2818,7 +2827,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // and its variables, to prevent it from being referenced by the constraint // system. if (!resultTy && - (!pattern->hasType() || pattern->getType()->hasUnboundGenericType())) { + (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType())) { pattern->setType(ErrorType::get(Context)); pattern->forEachVariable([&](VarDecl *var) { // Don't change the type of a variable that we've been able to @@ -2836,7 +2846,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, - unsigned patternNumber) { + unsigned patternNumber, + Type patternType) { Pattern *pattern = PBD->getPattern(patternNumber); Expr *init = PBD->getInit(patternNumber); @@ -2850,7 +2861,22 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, DC = initContext; } - bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC); + // If we weren't given a pattern type, compute one now. + if (!patternType) { + if (pattern->hasType()) + patternType = pattern->getType(); + else { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + patternType = typeCheckPattern(contextualPattern); + } + + if (patternType->hasError()) { + PBD->setInvalid(); + return true; + } + } + + bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC, patternType); if (!init) { PBD->setInvalid(); return true; @@ -2897,48 +2923,86 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /// The for-each statement. ForEachStmt *Stmt; + /// The declaration context in which this for-each statement resides. + DeclContext *DC; + /// The locator we're using. ConstraintLocator *Locator; + /// The contextual locator we're using. + ConstraintLocator *ContextualLocator; + + /// The Sequence protocol. + ProtocolDecl *SequenceProto; + + /// The IteratorProtocol. + ProtocolDecl *IteratorProto; + /// The type of the initializer. Type InitType; /// The type of the sequence. Type SequenceType; + /// The conformance of the sequence type to the Sequence protocol. + ProtocolConformanceRef SequenceConformance; + + /// The type of the element. + Type ElementType; + + /// The type of the iterator. + Type IteratorType; + public: - explicit BindingListener(ForEachStmt *stmt) : Stmt(stmt) { } + explicit BindingListener(ForEachStmt *stmt, DeclContext *dc) + : Stmt(stmt), DC(dc) { } bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { // Save the locator we're using for the expression. Locator = cs.getConstraintLocator(expr); - auto *contextualLocator = + ContextualLocator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); - // The expression type must conform to the Sequence. - ProtocolDecl *sequenceProto = TypeChecker::getProtocol( + // The expression type must conform to the Sequence protocol. + SequenceProto = TypeChecker::getProtocol( cs.getASTContext(), Stmt->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { + if (!SequenceProto) { return true; } - auto elementAssocType = - sequenceProto->getAssociatedType(cs.getASTContext().Id_Element); - SequenceType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), SequenceType, Locator); cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, - sequenceProto->getDeclaredType(), contextualLocator); + SequenceProto->getDeclaredType(), ContextualLocator); // Since we are using "contextual type" here, it has to be recorded // in the constraint system for diagnostics to have access to "purpose". cs.setContextualType( - expr, TypeLoc::withoutLoc(sequenceProto->getDeclaredType()), + expr, TypeLoc::withoutLoc(SequenceProto->getDeclaredType()), CTP_ForEachStmt); auto elementLocator = cs.getConstraintLocator( - contextualLocator, ConstraintLocator::SequenceElementType); + ContextualLocator, ConstraintLocator::SequenceElementType); + + // Check the element pattern. + ASTContext &ctx = cs.getASTContext(); + if (auto *P = TypeChecker::resolvePattern(Stmt->getPattern(), DC, + /*isStmtCondition*/false)) { + Stmt->setPattern(P); + } else { + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } + + auto contextualPattern = + ContextualPattern::forRawPattern(Stmt->getPattern(), DC); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { + // FIXME: Handle errors better. + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } // Collect constraints from the element pattern. auto pattern = Stmt->getPattern(); @@ -2948,10 +3012,34 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Add a conversion constraint between the element type of the sequence // and the type of the element pattern. - auto elementType = DependentMemberType::get(SequenceType, elementAssocType); - cs.addConstraint(ConstraintKind::Conversion, elementType, InitType, + auto elementAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Element); + ElementType = DependentMemberType::get(SequenceType, elementAssocType); + cs.addConstraint(ConstraintKind::Conversion, ElementType, InitType, elementLocator); + // Determine the iterator type. + auto iteratorAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Iterator); + IteratorType = DependentMemberType::get(SequenceType, iteratorAssocType); + + // The iterator type must conform to IteratorProtocol. + IteratorProto = TypeChecker::getProtocol( + cs.getASTContext(), Stmt->getForLoc(), + KnownProtocolKind::IteratorProtocol); + if (!IteratorProto) { + return true; + } + + // Reference the makeIterator witness. + FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); + Type makeIteratorType = + cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); + cs.addValueWitnessConstraint( + LValueType::get(SequenceType), makeIterator, + makeIteratorType, DC, FunctionRefKind::Compound, + ContextualLocator); + Stmt->setSequence(expr); return false; } @@ -2959,41 +3047,111 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { Expr *appliedSolution(Solution &solution, Expr *expr) override { // Figure out what types the constraints decided on. auto &cs = solution.getConstraintSystem(); + ASTContext &ctx = cs.getASTContext(); InitType = solution.simplifyType(InitType); SequenceType = solution.simplifyType(SequenceType); + ElementType = solution.simplifyType(ElementType); + IteratorType = solution.simplifyType(IteratorType); // Perform any necessary conversions of the sequence (e.g. [T]! -> [T]). - expr = solution.coerceToType(expr, SequenceType, cs.getConstraintLocator(expr)); - + expr = solution.coerceToType(expr, SequenceType, Locator); if (!expr) return nullptr; cs.cacheExprTypes(expr); + Stmt->setSequence(expr); + solution.setExprTypes(expr); // Apply the solution to the iteration pattern as well. Pattern *pattern = Stmt->getPattern(); TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; - if (TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(cs.DC), - InitType, options)) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType(contextualPattern, + InitType, options); + if (!pattern) return nullptr; + Stmt->setPattern(pattern); + + // Get the conformance of the sequence type to the Sequence protocol. + SequenceConformance = solution.resolveConformance( + ContextualLocator, SequenceProto); + assert(!SequenceConformance.isInvalid() && + "Couldn't find sequence conformance"); + Stmt->setSequenceConformance(SequenceConformance); + + // Check the filtering condition. + // FIXME: This should be pulled into the constraint system itself. + if (auto *Where = Stmt->getWhere()) { + if (!TypeChecker::typeCheckCondition(Where, DC)) + Stmt->setWhere(Where); } - Stmt->setPattern(pattern); - Stmt->setSequence(expr); + // Invoke iterator() to get an iterator from the sequence. + VarDecl *iterator; + Type nextResultType = OptionalType::get(ElementType); + { + // Create a local variable to capture the iterator. + std::string name; + if (auto np = dyn_cast_or_null(Stmt->getPattern())) + name = "$"+np->getBoundName().str().str(); + name += "$generator"; + + iterator = new (ctx) VarDecl( + /*IsStatic*/ false, VarDecl::Introducer::Var, + /*IsCaptureList*/ false, Stmt->getInLoc(), + ctx.getIdentifier(name), DC); + iterator->setInterfaceType(IteratorType->mapTypeOutOfContext()); + iterator->setImplicit(); + Stmt->setIteratorVar(iterator); + + auto genPat = new (ctx) NamedPattern(iterator); + genPat->setImplicit(); + + // TODO: test/DebugInfo/iteration.swift requires this extra info to + // be around. + PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, genPat, + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType), + DC, /*VarLoc*/ Stmt->getForLoc()); + } + + // Create the iterator variable. + auto *varRef = TypeChecker::buildCheckedRefExpr( + iterator, DC, DeclNameLoc(Stmt->getInLoc()), /*implicit*/ true); + if (varRef) + Stmt->setIteratorVarRef(varRef); + + // Convert that Optional value to the type of the pattern. + auto optPatternType = OptionalType::get(Stmt->getPattern()->getType()); + if (!optPatternType->isEqual(nextResultType)) { + OpaqueValueExpr *elementExpr = + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType, + /*isPlaceholder=*/true); + Expr *convertElementExpr = elementExpr; + if (TypeChecker::typeCheckExpression( + convertElementExpr, DC, + TypeLoc::withoutLoc(optPatternType), + CTP_CoerceOperand).isNull()) { + return nullptr; + } + elementExpr->setIsPlaceholder(false); + Stmt->setElementExpr(elementExpr); + Stmt->setConvertElementExpr(convertElementExpr); + } - solution.setExprTypes(expr); return expr; } }; - BindingListener listener(stmt); + BindingListener listener(stmt, dc); Expr *seq = stmt->getSequence(); assert(seq && "type-checking an uninitialized for-each statement?"); // Type-check the for-each loop sequence and element pattern. auto resultTy = TypeChecker::typeCheckExpression(seq, dc, &listener); - return !resultTy; + if (!resultTy) + return true; + return false; } bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { @@ -3031,6 +3189,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, hadAnyFalsable = true; continue; } + assert(elt.getKind() != StmtConditionElement::CK_Boolean); // This is cleanup goop run on the various paths where type checking of the // pattern binding fails. @@ -3059,10 +3218,9 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // Check the pattern, it allows unspecified types because the pattern can // provide type information. - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::typeCheckPattern(pattern, dc, options)) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { typeCheckPatternFailed(); continue; } @@ -3070,7 +3228,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // If the pattern didn't get a type, it's because we ran into some // unknown types along the way. We'll need to check the initializer. auto init = elt.getInitializer(); - hadError |= TypeChecker::typeCheckBinding(pattern, init, dc); + hadError |= TypeChecker::typeCheckBinding(pattern, init, dc, patternType); elt.setPattern(pattern); elt.setInitializer(init); hadAnyFalsable |= pattern->isRefutablePattern(); @@ -3426,88 +3584,6 @@ TypeChecker::coerceToRValue(ASTContext &Context, Expr *expr, return expr; } -bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern) { - // TODO: need to add kind arg? - // Construct a constraint system from this expression. - ConstraintSystem cs(dc, ConstraintSystemFlags::AllowFixes); - - // Cache the expression type on the system to ensure it is available - // on diagnostics if the convertion fails. - cs.cacheExprTypes(expr); - - // If there is a type that we're expected to convert to, add the conversion - // constraint. - cs.addConstraint(ConstraintKind::Conversion, expr->getType(), type, - cs.getConstraintLocator(expr)); - - auto &Context = dc->getASTContext(); - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Initial constraints for the given expression---\n"; - expr->dump(log); - log << "\n"; - cs.print(log); - } - - // Attempt to solve the constraint system. - SmallVector viable; - if ((cs.solve(viable) || viable.size() != 1)) { - // Try to fix the system or provide a decent diagnostic. - auto salvagedResult = cs.salvage(); - switch (salvagedResult.getKind()) { - case SolutionResult::Kind::Success: - viable.clear(); - viable.push_back(std::move(salvagedResult).takeSolution()); - break; - - case SolutionResult::Kind::Error: - case SolutionResult::Kind::Ambiguous: - return true; - - case SolutionResult::Kind::UndiagnosedError: - cs.diagnoseFailureForExpr(expr); - salvagedResult.markAsDiagnosed(); - return true; - - case SolutionResult::Kind::TooComplex: - Context.Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - salvagedResult.markAsDiagnosed(); - return true; - } - } - - auto &solution = viable[0]; - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Solution---\n"; - solution.dump(log); - } - - cs.cacheExprTypes(expr); - - // Perform the conversion. - Expr *result = solution.coerceToType(expr, type, - cs.getConstraintLocator(expr), - typeFromPattern); - if (!result) { - return true; - } - - solution.setExprTypes(expr); - - if (Context.TypeCheckerOpts.DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Type-checked expression---\n"; - result->dump(log); - log << "\n"; - } - - expr = result; - return false; -} - //===----------------------------------------------------------------------===// // Debugging //===----------------------------------------------------------------------===// @@ -4074,27 +4150,32 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } - // Check for casts between specific concrete types that cannot succeed. - if (auto toElementType = ConstraintSystem::isArrayType(toType)) { - if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; + auto checkElementCast = [&](Type fromElt, Type toElt, + CheckedCastKind castKind) -> CheckedCastKind { + switch (typeCheckCheckedCast(fromElt, toElt, CheckedCastContextKind::None, + dc, SourceLoc(), nullptr, SourceRange())) { + case CheckedCastKind::Coercion: + return CheckedCastKind::Coercion; - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; + case CheckedCastKind::BridgingCoercion: + return CheckedCastKind::BridgingCoercion; - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::ArrayDowncast; + case CheckedCastKind::ArrayDowncast: + case CheckedCastKind::DictionaryDowncast: + case CheckedCastKind::SetDowncast: + case CheckedCastKind::ValueCast: + return castKind; - case CheckedCastKind::Unresolved: - return failed(); - } + case CheckedCastKind::Unresolved: + return failed(); + } + }; + + // Check for casts between specific concrete types that cannot succeed. + if (auto toElementType = ConstraintSystem::isArrayType(toType)) { + if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::ArrayDowncast); } } @@ -4164,24 +4245,31 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (auto toElementType = ConstraintSystem::isSetType(toType)) { if (auto fromElementType = ConstraintSystem::isSetType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; - - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; - - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::SetDowncast; + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::SetDowncast); + } + } - case CheckedCastKind::Unresolved: + if (auto toTuple = toType->getAs()) { + if (auto fromTuple = fromType->getAs()) { + if (fromTuple->getNumElements() != toTuple->getNumElements()) return failed(); + + for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) { + const auto &fromElt = fromTuple->getElement(i); + const auto &toElt = toTuple->getElement(i); + + if (fromElt.getName() != toElt.getName()) + return failed(); + + auto result = checkElementCast(fromElt.getType(), toElt.getType(), + CheckedCastKind::ValueCast); + + if (result == CheckedCastKind::Unresolved) + return result; } + + return CheckedCastKind::ValueCast; } } @@ -4453,11 +4541,13 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // type. This is handled in the runtime, so it doesn't need a special cast // kind. if (Context.LangOpts.EnableObjCInterop) { + auto nsObject = Context.getNSObjectType(); + auto nsErrorTy = Context.getNSErrorType(); + if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) { if (!conformsToProtocol(toType, errorTypeProto, dc, ConformanceCheckFlags::InExpression) .isInvalid()) { - auto nsErrorTy = Context.getNSErrorType(); if (nsErrorTy) { if (isSubtypeOf(fromType, nsErrorTy, dc) // Don't mask "always true" warnings if NSError is cast to @@ -4466,6 +4556,23 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, return CheckedCastKind::ValueCast; } } + + if (!conformsToProtocol(fromType, errorTypeProto, dc, + ConformanceCheckFlags::InExpression) + .isInvalid()) { + // Cast of an error-conforming type to NSError or NSObject. + if ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy))) + return CheckedCastKind::BridgingCoercion; + } + } + + // Any class-like type could be dynamically cast to NSObject or NSError + // via an Error conformance. + if (fromType->mayHaveSuperclass() && + ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy)))) { + return CheckedCastKind::ValueCast; } } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index ab00f3b15603c..a394d8671136a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -258,7 +258,7 @@ HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, bool anyObject = false; auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); for (auto &found : inherited) { - auto *protoDecl = dyn_cast(found.second); + auto *protoDecl = dyn_cast(found.Item); if (!protoDecl) continue; @@ -490,11 +490,11 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, // class-bound protocol. for (const auto found : allInheritedNominals) { // Superclass bound. - if (isa(found.second)) + if (isa(found.Item)) return true; // A protocol that might be class-constrained. - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.Item)) { if (proto->requiresClass()) return true; } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 2aa54dcd385a8..15cc86f0d2322 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1339,6 +1339,7 @@ namespace { // Differentiation-related attributes. UNINTERESTING_ATTR(Differentiable) UNINTERESTING_ATTR(Derivative) + UNINTERESTING_ATTR(Transpose) // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 4d6ccb0ea8c0d..100a1b30f7401 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1103,6 +1103,7 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { auto *superclassDecl = classDecl->getSuperclassDecl(); if (superclassDecl && + superclassDecl->getModuleContext() != classDecl->getModuleContext() && superclassDecl->hasMissingDesignatedInitializers()) return; diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index de310517555cb..7ad524f45df3f 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -277,13 +277,21 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclNameRef name, // Determine which type we looked through to find this result. Type foundInType; - if (auto *baseDC = found.getDeclContext()) { - if (!baseDC->isTypeContext()) { - baseDC = baseDC->getParent(); - assert(baseDC->isTypeContext()); + if (auto *typeDC = found.getDeclContext()) { + if (!typeDC->isTypeContext()) { + // If we don't have a type context this is an implicit 'self' reference. + if (auto *CE = dyn_cast(typeDC)) { + // If we found the result in a self capture, look through the capture. + assert(CE->getCapturedSelfDecl()); + typeDC = found.getValueDecl()->getDeclContext(); + } else { + // Otherwise, we must have the method context. + typeDC = typeDC->getParent(); + } + assert(typeDC->isTypeContext()); } foundInType = dc->mapTypeIntoContext( - baseDC->getDeclaredInterfaceType()); + typeDC->getDeclaredInterfaceType()); assert(foundInType && "bogus base declaration?"); } diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 63bff0af884f1..3d359a84fee94 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -24,6 +24,7 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/Support/SaveAndRestore.h" #include using namespace swift; @@ -664,12 +665,9 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC, return P; } -static bool validateTypedPattern(TypeResolution resolution, +static Type validateTypedPattern(TypeResolution resolution, TypedPattern *TP, TypeResolutionOptions options) { - if (TP->hasType()) - return TP->getType()->hasError(); - TypeLoc TL = TP->getTypeLoc(); bool hadError; @@ -678,7 +676,7 @@ static bool validateTypedPattern(TypeResolution resolution, // variable binding, then we can bind the opaque return type from the // property definition. auto &Context = resolution.getASTContext(); - if (auto opaqueRepr = dyn_cast(TL.getTypeRepr())) { + if (auto opaqueRepr = dyn_cast_or_null(TL.getTypeRepr())) { auto named = dyn_cast( TP->getSubPattern()->getSemanticsProvidingPattern()); if (named) { @@ -699,19 +697,44 @@ static bool validateTypedPattern(TypeResolution resolution, } if (hadError) { - TP->setType(ErrorType::get(Context)); - return hadError; + return ErrorType::get(Context); } - TP->setType(TL.getType()); - assert(!dyn_cast_or_null(TL.getTypeRepr())); + return TL.getType(); +} + +Type TypeChecker::typeCheckPattern(ContextualPattern pattern) { + DeclContext *dc = pattern.getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, PatternTypeRequest{pattern}, ErrorType::get(ctx)); +} + +/// Apply the contextual pattern's context to the type resolution options. +static TypeResolutionOptions applyContextualPatternOptions( + TypeResolutionOptions options, ContextualPattern pattern) { + if (pattern.allowsInference()) { + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; + } - return hadError; + return options; } -bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options) { +llvm::Expected PatternTypeRequest::evaluate( + Evaluator &evaluator, ContextualPattern pattern) const { + Pattern *P = pattern.getPattern(); + DeclContext *dc = pattern.getDeclContext(); + + TypeResolutionOptions options(pattern.getPatternBindingDecl() + ? TypeResolverContext::PatternBindingDecl + : TypeResolverContext::InExpression); + options = applyContextualPatternOptions(options, pattern); + if (!pattern.isTopLevel()) { + options = options.withoutContext(); + } + auto &Context = dc->getASTContext(); switch (P->getKind()) { // Type-check paren patterns by checking the sub-pattern and @@ -723,17 +746,15 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, SP = PP->getSubPattern(); else SP = cast(P)->getSubPattern(); - if (TypeChecker::typeCheckPattern(SP, dc, options)) { - P->setType(ErrorType::get(Context)); - return true; - } - if (SP->hasType()) { - auto type = SP->getType(); - if (P->getKind() == PatternKind::Paren) - type = ParenType::get(Context, type); - P->setType(type); - } - return false; + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(SP, /*retainTopLevel=*/true)); + if (subType->hasError()) + return ErrorType::get(Context); + + auto type = subType; + if (P->getKind() == PatternKind::Paren) + type = ParenType::get(Context, type); + return type; } // If we see an explicit type annotation, coerce the sub-pattern to @@ -741,23 +762,7 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, case PatternKind::Typed: { auto resolution = TypeResolution::forContextual(dc); TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(resolution, TP, options); - - // If we have unbound generic types, don't apply them below; instead, - // the caller will call typeCheckBinding() later. - if (P->getType()->hasUnboundGenericType()) - return hadError; - - Pattern *subPattern = TP->getSubPattern(); - if (TypeChecker::coercePatternToType(subPattern, resolution, P->getType(), - options|TypeResolutionFlags::FromNonInferredPattern, - TP->getTypeLoc())) - hadError = true; - else { - TP->setSubPattern(subPattern); - TP->setType(subPattern->getType()); - } - return hadError; + return validateTypedPattern(resolution, TP, options); } // A wildcard or name pattern cannot appear by itself in a context @@ -767,16 +772,15 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, // If we're type checking this pattern in a context that can provide type // information, then the lack of type information is not an error. if (options & TypeResolutionFlags::AllowUnspecifiedTypes) - return false; + return Context.TheUnresolvedType; Context.Diags.diagnose(P->getLoc(), diag::cannot_infer_type_for_pattern); - P->setType(ErrorType::get(Context)); if (auto named = dyn_cast(P)) { if (auto var = named->getDecl()) { var->setInvalid(); } } - return true; + return ErrorType::get(Context); // A tuple pattern propagates its tuple-ness out. case PatternKind::Tuple: { @@ -784,32 +788,21 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, bool hadError = false; SmallVector typeElts; - const auto elementOptions = options.withoutContext(); - bool missingType = false; for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { TuplePatternElt &elt = tuplePat->getElement(i); - Pattern *pattern = elt.getPattern(); - if (TypeChecker::typeCheckPattern(pattern, dc, elementOptions)) { + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false)); + if (subType->hasError()) hadError = true; - continue; - } - if (!pattern->hasType()) { - missingType = true; - continue; - } - typeElts.push_back(TupleTypeElt(pattern->getType(), elt.getLabel())); + typeElts.push_back(TupleTypeElt(subType, elt.getLabel())); } if (hadError) { - P->setType(ErrorType::get(Context)); - return true; + return ErrorType::get(Context); } - if (!missingType && !(options & - TypeResolutionFlags::AllowUnspecifiedTypes)) { - P->setType(TupleType::get(typeElts, Context)); - } - return false; + + return TupleType::get(typeElts, Context); } //--- Refutable patterns. @@ -825,11 +818,10 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, if (!(options & TypeResolutionFlags::AllowUnspecifiedTypes)) { Context.Diags.diagnose(P->getLoc(), diag::refutable_pattern_requires_initializer); - P->setType(ErrorType::get(Context)); - return true; + return ErrorType::get(Context); } - return false; + return Context.TheUnresolvedType; } llvm_unreachable("bad pattern kind!"); } @@ -875,18 +867,20 @@ void implicitlyUntuplePatternIfApplicable(DiagnosticEngine &DE, } /// Perform top-down type coercion on the given pattern. -bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, - Type type, - TypeResolutionOptions options, - TypeLoc tyLoc) { -recur: +Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, + Type type, + TypeResolutionOptions options, + TypeLoc tyLoc) { if (tyLoc.isNull()) { tyLoc = TypeLoc::withoutLoc(type); } - auto dc = resolution.getDeclContext(); + auto P = pattern.getPattern(); + auto dc = pattern.getDeclContext(); auto &Context = dc->getASTContext(); auto &diags = Context.Diags; + + options = applyContextualPatternOptions(options, pattern); auto subOptions = options; subOptions.setContext(None); switch (P->getKind()) { @@ -904,44 +898,53 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (tupleType->getNumElements() == 1 && !tupleType->getElement(0).isVararg()) { auto elementTy = tupleType->getElementType(0); - if (coercePatternToType(sub, resolution, elementTy, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/true), elementTy, + subOptions); + if (!sub) + return nullptr; TuplePatternElt elt(sub); P = TuplePattern::create(Context, PP->getLParenLoc(), elt, PP->getRParenLoc()); if (PP->isImplicit()) P->setImplicit(); P->setType(type); - return false; + return P; } } } - - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; + PP->setSubPattern(sub); PP->setType(sub->getType()); - return false; + return P; } case PatternKind::Var: { auto VP = cast(P); Pattern *sub = VP->getSubPattern(); - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; VP->setSubPattern(sub); if (sub->hasType()) VP->setType(sub->getType()); - return false; + return P; } // If we see an explicit type annotation, coerce the sub-pattern to // that type. case PatternKind::Typed: { TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(resolution, TP, options); - if (!hadError) { - if (!type->isEqual(TP->getType()) && !type->hasError()) { + Type patternType = TypeChecker::typeCheckPattern(pattern); + bool hadError = false; + if (!patternType->hasError()) { + if (!type->isEqual(patternType) && !type->hasError()) { if (options & TypeResolutionFlags::OverrideType) { TP->setType(type); } else { @@ -950,16 +953,20 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, hadError = true; } } + } else { + hadError = true; } Pattern *sub = TP->getSubPattern(); - hadError |= coercePatternToType(sub, resolution, TP->getType(), - subOptions | TypeResolutionFlags::FromNonInferredPattern); - if (!hadError) { - TP->setSubPattern(sub); - TP->setType(sub->getType()); - } - return hadError; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions | TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + + TP->setSubPattern(sub); + TP->setType(sub->getType()); + return P; } // For wildcard and name patterns, set the type. @@ -1026,11 +1033,11 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, .fixItInsertAfter(var->getNameLoc(), ": " + type->getWithoutParens()->getString()); } - return false; + return P; } case PatternKind::Any: P->setType(type); - return false; + return P; // We can match a tuple pattern with a tuple type. // TODO: permit implicit conversions? @@ -1041,12 +1048,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Sometimes a paren is just a paren. If the tuple pattern has a single // element, we can reduce it to a paren pattern. bool canDecayToParen = TP->getNumElements() == 1; - auto decayToParen = [&]() -> bool { + auto decayToParen = [&]() -> Pattern * { assert(canDecayToParen); Pattern *sub = TP->getElement(0).getPattern(); - if (TypeChecker::coercePatternToType(sub, resolution, type, subOptions)) - return true; - + sub = TypeChecker::coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions); + if (!sub) + return nullptr; + if (TP->getLParenLoc().isValid()) { P = new (Context) ParenPattern(TP->getLParenLoc(), sub, TP->getRParenLoc(), @@ -1055,7 +1065,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else { P = sub; } - return false; + return P; }; // The context type must be a tuple. @@ -1083,7 +1093,6 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, for (unsigned i = 0, e = TP->getNumElements(); i != e; ++i) { TuplePatternElt &elt = TP->getElement(i); - Pattern *pattern = elt.getPattern(); Type CoercionType; if (hadError) @@ -1099,14 +1108,21 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, elt.getLabel(), tupleTy->getElement(i).getName()); hadError = true; } - - hadError |= coercePatternToType(pattern, resolution, CoercionType, - options); + + auto sub = coercePatternToType( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false), + CoercionType, subOptions); + if (!sub) + return nullptr; + if (!hadError) - elt.setPattern(pattern); + elt.setPattern(sub); } - return hadError; + if (hadError) + return nullptr; + + return P; } // Coerce expressions by finding a '~=' operator that can compare the @@ -1122,7 +1138,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, EP->getSubExpr()->getSemanticsProvidingExpr())) { P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); P->setType(type); - return false; + return P; } } @@ -1136,10 +1152,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, DeclNameLoc(NLE->getLoc()), NoneEnumElement->createNameRef(), NoneEnumElement, nullptr, false); - return TypeChecker::coercePatternToType(P, resolution, type, options); + return TypeChecker::coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); } } - return TypeChecker::typeCheckExprPattern(cast(P), dc, type); + + if (TypeChecker::typeCheckExprPattern(cast(P), dc, type)) + return nullptr; + + return P; } // Coerce an 'is' pattern by determining the cast kind. @@ -1147,9 +1168,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto IP = cast(P); // Type-check the type parameter. - TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolution resolution = TypeResolution::forContextual(dc); if (validateType(Context, IP->getCastTypeLoc(), resolution, paramOptions)) - return true; + return nullptr; auto castType = IP->getCastTypeLoc().getType(); @@ -1175,7 +1197,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } P = sub; - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevle=*/true), type, options); } @@ -1190,7 +1213,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, IP->getCastTypeLoc().getSourceRange()); switch (castKind) { case CheckedCastKind::Unresolved: - return true; + return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: // If this is an 'as' pattern coercing between two different types, then @@ -1212,7 +1235,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(IP->getLoc(), diag::isa_collection_downcast_pattern_value_unimplemented, IP->getCastTypeLoc().getType()); - return false; + return P; } case CheckedCastKind::ValueCast: @@ -1223,13 +1246,17 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Coerce the subpattern to the destination type. if (Pattern *sub = IP->getSubPattern()) { - if (coercePatternToType(sub, resolution, IP->getCastTypeLoc().getType(), - subOptions|TypeResolutionFlags::FromNonInferredPattern)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), + IP->getCastTypeLoc().getType(), + subOptions|TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + IP->setSubPattern(sub); } - return false; + return P; } case PatternKind::EnumElement: { @@ -1266,7 +1293,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, /*message*/ StringRef()) .fixItReplace(EEP->getLoc(), Rename.str()); - return true; + return nullptr; } // If we have the original expression parse tree, try reinterpreting @@ -1275,7 +1302,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else if (EEP->hasUnresolvedOriginalExpr()) { P = new (Context) ExprPattern(EEP->getUnresolvedOriginalExpr(), nullptr, nullptr); - goto recur; + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } // If we have an optional type, let's try to see if the case @@ -1289,19 +1318,21 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, EEP->getName(), EEP->getLoc())) { P = new (Context) OptionalSomePattern(EEP, EEP->getEndLoc(), /*implicit*/true); - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } else { diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found, EEP->getName(), type); - return true; + return nullptr; } } } } if (!elt) - return true; + return nullptr; enumTy = type; } else { @@ -1323,7 +1354,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else { diags.diagnose(EEP->getLoc(), diag::ambiguous_enum_pattern_type, parentTy, type); - return true; + return nullptr; } } // Otherwise, see if we can introduce a cast pattern to get from an @@ -1337,7 +1368,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, nullptr, SourceRange()); // If the cast failed, we can't resolve the pattern. if (foundCastKind < CheckedCastKind::First_Resolved) - return true; + return nullptr; // Otherwise, we can type-check as the enum type, and insert a cast // from the outer pattern type. @@ -1347,7 +1378,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_not_member_of_enum, EEP->getName(), type); - return true; + return nullptr; } } @@ -1362,7 +1393,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_assoc_values_remove) .fixItRemove(sub->getSourceRange()); - return true; + return nullptr; } Type elementType; @@ -1377,8 +1408,12 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, ::implicitlyUntuplePatternIfApplicable(Context.Diags, sub, elementType); - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; + EEP->setSubPattern(sub); } else if (argType) { // Else if the element pattern has no sub-pattern but the element type has @@ -1408,8 +1443,11 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; EEP->setSubPattern(sub); } @@ -1431,7 +1469,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, P = isPattern; } - return false; + return P; } case PatternKind::OptionalSome: { @@ -1448,12 +1486,12 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } diags.diagnose(loc, diagID, type); - return true; + return nullptr; } EnumElementDecl *elementDecl = Context.getOptionalSomeDecl(); if (!elementDecl) - return true; + return nullptr; OP->setElementDecl(elementDecl); @@ -1461,21 +1499,24 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; + OP->setSubPattern(sub); OP->setType(type); - return false; + return P; } case PatternKind::Bool: P->setType(type); - return false; + return P; } llvm_unreachable("bad pattern kind!"); } - /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index 2226037b40418..ef4a5aab3dc9e 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -256,8 +256,9 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { unsigned discriminator = DF.getNextDiscriminator(); ClosureExpr *CE = - new (Context) ClosureExpr(params, SourceLoc(), SourceLoc(), SourceLoc(), - TypeLoc(), discriminator, newTopLevel); + new (Context) ClosureExpr(SourceRange(), nullptr, params, SourceLoc(), + SourceLoc(), SourceLoc(), TypeLoc(), + discriminator, newTopLevel); SmallVector args; params->getParams(args); @@ -299,7 +300,7 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { newTopLevel->setBody(BS); TypeChecker::checkTopLevelErrorHandling(newTopLevel); - SF.Decls.push_back(newTopLevel); + SF.addTopLevelDecl(newTopLevel); } /// When we see an expression in a TopLevelCodeDecl in the REPL, process it, @@ -322,7 +323,7 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { // Remove the expression from being in the list of decls to execute, we're // going to reparent it. - auto TLCD = cast(SF.Decls.back()); + auto TLCD = cast(SF.getTopLevelDecls().back()); E = TypeChecker::coerceToRValue(Context, E); @@ -332,7 +333,7 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { /*IsCaptureList*/false, E->getStartLoc(), name, &SF); vd->setInterfaceType(E->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); @@ -396,8 +397,8 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // replPrint(r123) // Remove PBD from the list of Decls so we can insert before it. - auto PBTLCD = cast(SF.Decls.back()); - SF.Decls.pop_back(); + auto PBTLCD = cast(SF.getTopLevelDecls().back()); + SF.truncateTopLevelDecls(SF.getTopLevelDecls().size() - 1); // Create the meta-variable, let the typechecker name it. Identifier name = getNextResponseVariableName(SF.getParentModule()); @@ -406,7 +407,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { /*IsCaptureList*/false, PBD->getStartLoc(), name, &SF); vd->setInterfaceType(pattern->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); @@ -421,7 +422,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { metavarBinding->getEndLoc()); auto *MVTLCD = new (Context) TopLevelCodeDecl(&SF, MVBrace); - SF.Decls.push_back(MVTLCD); + SF.addTopLevelDecl(MVTLCD); // Replace the initializer of PBD with a reference to our repl temporary. @@ -430,7 +431,7 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { /*Implicit=*/true); E = TypeChecker::coerceToRValue(Context, E); PBD->setInit(entryIdx, E); - SF.Decls.push_back(PBTLCD); + SF.addTopLevelDecl(PBTLCD); // Finally, print out the result, by referring to the repl temp. E = TypeChecker::buildCheckedRefExpr(vd, &SF, @@ -465,19 +466,20 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, unsigned FirstDecl) { // Walk over all decls in the file to find the next available closure // discriminator. DiscriminatorFinder DF; - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) D->walk(DF); // Move new declarations out. - std::vector NewDecls(SF.Decls.begin()+FirstDecl, SF.Decls.end()); - SF.Decls.resize(FirstDecl); + std::vector NewDecls(SF.getTopLevelDecls().begin()+FirstDecl, + SF.getTopLevelDecls().end()); + SF.truncateTopLevelDecls(FirstDecl); REPLChecker RC(SF, DF); // Loop over each of the new decls, processing them, adding them back to // the Decls list. for (Decl *D : NewDecls) { - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); auto *TLCD = dyn_cast(D); if (!TLCD || TLCD->getBody()->getElements().empty()) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index e941374e1ee7d..20ec78994476b 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -731,166 +731,9 @@ class StmtChecker : public StmtVisitor { } Stmt *visitForEachStmt(ForEachStmt *S) { - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - - if (auto *P = TypeChecker::resolvePattern(S->getPattern(), DC, - /*isStmtCondition*/false)) { - S->setPattern(P); - } else { - S->getPattern()->setType(ErrorType::get(getASTContext())); - return nullptr; - } - - if (TypeChecker::typeCheckPattern(S->getPattern(), DC, options)) { - // FIXME: Handle errors better. - S->getPattern()->setType(ErrorType::get(getASTContext())); - return nullptr; - } - if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; - if (auto *Where = S->getWhere()) { - if (TypeChecker::typeCheckCondition(Where, DC)) - return nullptr; - S->setWhere(Where); - } - - - // Retrieve the 'Sequence' protocol. - ProtocolDecl *sequenceProto = TypeChecker::getProtocol( - getASTContext(), S->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { - return nullptr; - } - - // Retrieve the 'Iterator' protocol. - ProtocolDecl *iteratorProto = TypeChecker::getProtocol( - getASTContext(), S->getForLoc(), KnownProtocolKind::IteratorProtocol); - if (!iteratorProto) { - return nullptr; - } - - Expr *sequence = S->getSequence(); - - // Invoke iterator() to get an iterator from the sequence. - Type iteratorTy; - VarDecl *iterator; - { - Type sequenceType = sequence->getType(); - auto conformance = - TypeChecker::conformsToProtocol(sequenceType, sequenceProto, DC, - ConformanceCheckFlags::InExpression, - sequence->getLoc()); - if (conformance.isInvalid()) - return nullptr; - S->setSequenceConformance(conformance); - - iteratorTy = - conformance.getTypeWitnessByName(sequenceType, - getASTContext().Id_Iterator); - if (iteratorTy->hasError()) - return nullptr; - - auto witness = - conformance.getWitnessByName(sequenceType, - getASTContext().Id_makeIterator); - if (!witness) - return nullptr; - S->setMakeIterator(witness); - - // Create a local variable to capture the iterator. - std::string name; - if (auto np = dyn_cast_or_null(S->getPattern())) - name = "$"+np->getBoundName().str().str(); - name += "$generator"; - - iterator = new (getASTContext()) VarDecl( - /*IsStatic*/ false, VarDecl::Introducer::Var, - /*IsCaptureList*/ false, S->getInLoc(), - getASTContext().getIdentifier(name), DC); - iterator->setInterfaceType(iteratorTy->mapTypeOutOfContext()); - iterator->setImplicit(); - S->setIteratorVar(iterator); - - auto genPat = new (getASTContext()) NamedPattern(iterator); - genPat->setImplicit(); - - // TODO: test/DebugInfo/iteration.swift requires this extra info to - // be around. - auto nextResultType = OptionalType::get(conformance.getTypeWitnessByName( - sequenceType, getASTContext().Id_Element)); - PatternBindingDecl::createImplicit( - getASTContext(), StaticSpellingKind::None, genPat, - new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType), - DC, /*VarLoc*/ S->getForLoc()); - - Type newSequenceType = cast(witness.getDecl()) - ->getInterfaceType() - ->castTo() - ->getParams()[0].getPlainType().subst(witness.getSubstitutions()); - - // Necessary type coersion for method application. - if (TypeChecker::convertToType(sequence, newSequenceType, DC, None)) { - return nullptr; - } - S->setSequence(sequence); - } - - // Working with iterators requires Optional. - if (TypeChecker::requireOptionalIntrinsics(getASTContext(), S->getForLoc())) - return nullptr; - - // Gather the witnesses from the Iterator protocol conformance, which - // we'll use to drive the loop. - // FIXME: Would like to customize the diagnostic emitted in - // conformsToProtocol(). - auto genConformance = TypeChecker::conformsToProtocol( - iteratorTy, iteratorProto, DC, ConformanceCheckFlags::InExpression, - sequence->getLoc()); - if (genConformance.isInvalid()) - return nullptr; - - Type elementTy = - genConformance.getTypeWitnessByName(iteratorTy, - getASTContext().Id_Element); - if (elementTy->hasError()) - return nullptr; - - auto *varRef = TypeChecker::buildCheckedRefExpr(iterator, DC, - DeclNameLoc(S->getInLoc()), - /*implicit*/ true); - if (!varRef) - return nullptr; - - S->setIteratorVarRef(varRef); - - auto witness = - genConformance.getWitnessByName(iteratorTy, getASTContext().Id_next); - if (!witness) - return nullptr; - S->setIteratorNext(witness); - - auto nextResultType = cast(S->getIteratorNext().getDecl()) - ->getResultInterfaceType() - .subst(S->getIteratorNext().getSubstitutions()); - - // Convert that Optional value to Optional. - auto optPatternType = OptionalType::get(S->getPattern()->getType()); - if (!optPatternType->isEqual(nextResultType)) { - OpaqueValueExpr *elementExpr = - new (getASTContext()) OpaqueValueExpr(S->getInLoc(), nextResultType); - Expr *convertElementExpr = elementExpr; - if (TypeChecker::convertToType(convertElementExpr, optPatternType, DC, - S->getPattern())) { - return nullptr; - } - S->setElementExpr(elementExpr); - S->setConvertElementExpr(convertElementExpr); - } - // Type-check the body of the loop. AddLabeledStmt loopNest(*this, S); BraceStmt *Body = S->getBody(); @@ -1088,12 +931,21 @@ class StmtChecker : public StmtVisitor { } pattern = newPattern; + // Coerce the pattern to the subject's type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!subjectType || - TypeChecker::coercePatternToType(pattern, - TypeResolution::forContextual(DC), - subjectType, patternOptions)) { + bool coercionError = false; + if (subjectType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, subjectType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!subjectType || coercionError) { limitExhaustivityChecks = true; // If that failed, mark any variables binding pieces of the pattern @@ -1497,10 +1349,19 @@ bool TypeChecker::typeCheckCatchPattern(CatchStmt *S, DeclContext *DC) { pattern = newPattern; // Coerce the pattern to the exception type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!exnType || - coercePatternToType(pattern, TypeResolution::forContextual(DC), exnType, - patternOptions)) { + bool coercionError = false; + if (exnType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = coercePatternToType( + contextualPattern, exnType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!exnType || coercionError) { // If that failed, be sure to give the variables error types // before we type-check the guard. (This will probably kill // most of the type-checking, but maybe not.) @@ -2119,12 +1980,17 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, if (!body || AFD->isBodyTypeChecked()) return false; + bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { if (Type builderType = getFunctionBuilderType(func)) { - body = TypeChecker::applyFunctionBuilderBodyTransform(func, body, - builderType); - if (!body) - return true; + if (auto optBody = + TypeChecker::applyFunctionBuilderBodyTransform(func, builderType)) { + if (!*optBody) + return true; + + body = *optBody; + alreadyTypeChecked = true; + } } else if (func->hasSingleExpressionBody() && func->getResultInterfaceType()->isVoid()) { // The function returns void. We don't need an explicit return, no matter @@ -2152,9 +2018,13 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, if (ctx.LangOpts.EnableASTScopeLookup) ASTScope::expandFunctionBody(AFD); - StmtChecker SC(AFD); - SC.EndTypeCheckLoc = endTypeCheckLoc; - bool hadError = SC.typeCheckBody(body); + // Type check the function body if needed. + bool hadError = false; + if (!alreadyTypeChecked) { + StmtChecker SC(AFD); + SC.EndTypeCheckLoc = endTypeCheckLoc; + hadError = SC.typeCheckBody(body); + } // If this was a function with a single expression body, let's see // if implicit return statement came out to be `Never` which means diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 18d7c7e96b08b..6d490fda71ac0 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -17,6 +17,7 @@ #include "CodeSynthesis.h" #include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "TypeCheckDecl.h" #include "TypeCheckType.h" #include "swift/AST/ASTContext.h" @@ -205,6 +206,8 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // In particular, it's /not/ correct to check the PBD's DeclContext because // top-level variables in a script file are accessible from other files, // even though the PBD is inside a TopLevelCodeDecl. + auto contextualPattern = + ContextualPattern::forPatternBindingDecl(binding, entryNumber); TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); if (binding->isInitialized(entryNumber)) { @@ -213,7 +216,8 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, options |= TypeResolutionFlags::AllowUnboundGenerics; } - if (TypeChecker::typeCheckPattern(pattern, binding->getDeclContext(), options)) { + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { swift::setBoundVarsTypeError(pattern, Context); binding->setInvalid(); pattern->setType(ErrorType::get(Context)); @@ -224,20 +228,20 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // default-initializable. If so, do it. if (!pbe.isInitialized() && binding->isDefaultInitializable(entryNumber) && - pattern->hasStorage() && - !pattern->getType()->hasError()) { - auto type = pattern->getType(); - if (auto defaultInit = TypeChecker::buildDefaultInitializer(type)) { + pattern->hasStorage()) { + if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) { // If we got a default initializer, install it and re-type-check it // to make sure it is properly coerced to the pattern type. binding->setInit(entryNumber, defaultInit); } } - // If the pattern didn't get a type or if it contains an unbound generic type, - // we'll need to check the initializer. - if (!pattern->hasType() || pattern->getType()->hasUnboundGenericType()) { - if (TypeChecker::typeCheckPatternBinding(binding, entryNumber)) { + // If the pattern contains some form of unresolved type, we'll need to + // check the initializer. + if (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType()) { + if (TypeChecker::typeCheckPatternBinding(binding, entryNumber, + patternType)) { binding->setInvalid(); return &pbe; } @@ -250,6 +254,17 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, binding->diagnose(diag::inferred_opaque_type, binding->getInit(entryNumber)->getType()); } + } else { + // Coerce the pattern to the computed type. + if (auto newPattern = TypeChecker::coercePatternToType( + contextualPattern, patternType, + TypeResolverContext::PatternBindingDecl)) { + pattern = newPattern; + } else { + binding->setInvalid(); + pattern->setType(ErrorType::get(Context)); + return &pbe; + } } // If the pattern binding appears in a type or library file context, then @@ -586,7 +601,9 @@ getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) { return result; } -/// Build an l-value for the storage of a declaration. +/// Build an l-value for the storage of a declaration. Returns nullptr if there +/// was an error. This should only occur if an invalid declaration was type +/// checked; another diagnostic should have been emitted already. static Expr *buildStorageReference(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, @@ -673,12 +690,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -694,7 +707,20 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // Perform accesses to the wrappedValues along the composition chain. for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) { auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i); - underlyingVars.push_back(wrapperInfo.valueVar); + auto wrappedValue = wrapperInfo.valueVar; + + // Check for availability of wrappedValue. + if (accessor->getAccessorKind() == AccessorKind::Get || + accessor->getAccessorKind() == AccessorKind::Read) { + if (auto *attr = wrappedValue->getAttrs().getUnavailable(ctx)) { + diagnoseExplicitUnavailability( + wrappedValue, + var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), + var->getDeclContext(), nullptr); + } + } + + underlyingVars.push_back(wrappedValue); } semantics = AccessSemantics::DirectToStorage; selfAccessKind = SelfAccessorKind::Peer; @@ -707,12 +733,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -822,7 +844,12 @@ static Expr *buildStorageReference(AccessorDecl *accessor, ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getFullName().getArgumentNames(), { }, SourceLoc(), nullptr, subscriptDecl, /*Implicit=*/true); - TypeChecker::typeCheckExpression(lookupExpr, accessor); + + // FIXME: Since we're not resolving overloads or anything, we should be + // building fully type-checked AST above; we already have all the + // information that we need. + if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) + return nullptr; // Make sure we produce an lvalue only when desired. if (isMemberLValue != lookupExpr->getType()->is()) { @@ -1020,6 +1047,10 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, Expr *dest = buildStorageReference(accessor, storage, target, /*isLValue=*/true, ctx); + // Error recovery. + if (dest == nullptr) + return; + // A lazy property setter will store a value of type T into underlying storage // of type T?. auto destType = dest->getType()->getWithoutSpecifierType(); @@ -1029,6 +1060,7 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, return; if (!destType->isEqual(value->getType())) { + assert(destType->getOptionalObjectType()); assert(destType->getOptionalObjectType()->isEqual(value->getType())); value = new (ctx) InjectIntoOptionalExpr(value, destType); } @@ -1064,10 +1096,15 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, Expr *result = createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); - ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, - /*IsImplicit=*/true); - return { BraceStmt::create(ctx, loc, returnStmt, loc, true), + SmallVector body; + if (result != nullptr) { + ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, + /*IsImplicit=*/true); + body.push_back(returnStmt); + } + + return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; } @@ -1459,7 +1496,13 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, if (VD->getParsedAccessor(AccessorKind::DidSet)) { Expr *OldValueExpr = buildStorageReference(Set, VD, target, /*isLValue=*/true, Ctx); - OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + + // Error recovery. + if (OldValueExpr == nullptr) { + OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType()); + } else { + OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + } OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, /*IsCaptureList*/false, SourceLoc(), @@ -1587,13 +1630,14 @@ synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { // Build a reference to the storage. Expr *ref = buildStorageReference(accessor, storage, target, isLValue, ctx); + if (ref != nullptr) { + // Wrap it with an `&` marker if this is a modify. + ref = maybeWrapInOutExpr(ref, ctx); - // Wrap it with an `&` marker if this is a modify. - ref = maybeWrapInOutExpr(ref, ctx); - - // Yield it. - YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); - body.push_back(yield); + // Yield it. + YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); + body.push_back(yield); + } return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; @@ -1995,22 +2039,22 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, // Getters and setters for lazy properties are not @_transparent. if (storage->getAttrs().hasAttribute()) return false; + } - // Getters/setters for a property with a wrapper are not @_transparent if - // the backing variable has more-restrictive access than the original - // property. The same goes for its storage wrapper. - if (auto var = dyn_cast(storage)) { - if (auto backingVar = var->getPropertyWrapperBackingProperty()) { - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + // Accessors for a property with a wrapper are not @_transparent if + // the backing variable has more-restrictive access than the original + // property. The same goes for its storage wrapper. + if (auto var = dyn_cast(storage)) { + if (auto backingVar = var->getPropertyWrapperBackingProperty()) { + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; + } - if (auto original = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { - auto backingVar = original->getPropertyWrapperBackingProperty(); - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + if (auto original = var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { + auto backingVar = original->getPropertyWrapperBackingProperty(); + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; } } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d1e911e0aa66a..e5c0cb67970d0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1644,6 +1644,10 @@ Type TypeChecker::resolveIdentifierType( if (!result) return nullptr; if (auto moduleTy = result->getAs()) { + // Allow module types only if flag is specified. + if (options.contains(TypeResolutionFlags::AllowModule)) + return moduleTy; + // Otherwise, emit an error. if (!options.contains(TypeResolutionFlags::SilenceErrors)) { auto moduleName = moduleTy->getModule()->getName(); diags.diagnose(Components.back()->getNameLoc(), @@ -1778,6 +1782,14 @@ namespace { return diags.diagnose(std::forward(Args)...); } + template + InFlightDiagnostic diagnoseInvalid(TypeRepr *repr, + ArgTypes &&... Args) const { + auto &diags = Context.Diags; + repr->setInvalid(); + return diags.diagnose(std::forward(Args)...); + } + Type resolveAttributedType(AttributedTypeRepr *repr, TypeResolutionOptions options); Type resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, @@ -1787,12 +1799,15 @@ namespace { AnyFunctionType::Representation representation = AnyFunctionType::Representation::Swift, bool noescape = false, + const clang::Type *parsedClangFunctionType + = nullptr, DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable); bool resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, TypeResolutionOptions options, bool requiresMappingOut, + DifferentiabilityKind diffKind, SmallVectorImpl &ps); Type resolveSILFunctionType(FunctionTypeRepr *repr, @@ -2026,6 +2041,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Remember whether this is a function parameter. bool isParam = options.is(TypeResolverContext::FunctionInput); + // Remember whether this is a variadic function parameter. + bool isVariadicFunctionParam = + options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl); + // The type we're working with, in case we want to build it differently // based on the attributes we see. Type ty; @@ -2070,18 +2090,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Check for @thick. if (attrs.has(TAK_thick)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } + storedRepr = MetatypeRepresentation::Thick; attrs.clearAttribute(TAK_thick); } // Check for @objc_metatype. if (attrs.has(TAK_objc_metatype)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } storedRepr = MetatypeRepresentation::ObjC; attrs.clearAttribute(TAK_objc_metatype); } @@ -2109,8 +2132,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto checkUnsupportedAttr = [&](TypeAttrKind attr) { if (attrs.has(attr)) { - diagnose(attrs.getLoc(attr), diag::unknown_attribute, - TypeAttributes::getAttrName(attr)); + diagnoseInvalid(repr, attrs.getLoc(attr), diag::unknown_attribute, + TypeAttributes::getAttrName(attr)); attrs.clearAttribute(attr); } }; @@ -2145,7 +2168,29 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Function attributes require a syntactic function type. auto *fnRepr = dyn_cast(repr); + auto tryParseClangType = [this](TypeAttributes::Convention &conv, + bool hasConventionCOrBlock) + -> const clang::Type * { + if (conv.ClangType.Item.empty()) + return nullptr; + if (!hasConventionCOrBlock) { + diagnose(conv.ClangType.Loc, + diag::unexpected_ctype_for_non_c_convention, + conv.Name, conv.ClangType.Item); + return nullptr; + } + + const clang::Type *type = Context.getClangModuleLoader() + ->parseClangFunctionType(conv.ClangType.Item, + conv.ClangType.Loc); + if (!type) + diagnose(conv.ClangType.Loc, diag::unable_to_parse_c_function_type, + conv.ClangType.Item); + return type; + }; + if (fnRepr && hasFunctionAttr) { + const clang::Type *parsedClangFunctionType = nullptr; if (options & TypeResolutionFlags::SILType) { SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; @@ -2160,8 +2205,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto calleeConvention = ParameterConvention::Direct_Unowned; if (attrs.has(TAK_callee_owned)) { if (attrs.has(TAK_callee_guaranteed)) { - diagnose(attrs.getLoc(TAK_callee_owned), - diag::sil_function_repeat_convention, /*callee*/ 2); + diagnoseInvalid(repr, attrs.getLoc(TAK_callee_owned), + diag::sil_function_repeat_convention, /*callee*/ 2); } calleeConvention = ParameterConvention::Direct_Owned; } else if (attrs.has(TAK_callee_guaranteed)) { @@ -2187,11 +2232,17 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, SILFunctionType::Representation::WitnessMethod) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), - diag::unsupported_sil_convention, attrs.getConventionName()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_sil_convention, + attrs.getConventionName()); rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; + bool isCOrBlock = + rep == SILFunctionTypeRepresentation::CFunctionPointer + || rep == SILFunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } if (rep == SILFunctionType::Representation::WitnessMethod) { @@ -2204,8 +2255,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_differentiable), - diag::experimental_differentiable_programming_disabled); + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; @@ -2236,42 +2287,24 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, .Case("c", FunctionType::Representation::CFunctionPointer) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), diag::unsupported_convention, - attrs.getConventionName()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_convention, + attrs.getConventionName()); rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; - - if (attrs.has(TAK_autoclosure)) { - // @convention(c) and @convention(block) are not allowed with an @autoclosure type. - if (rep == FunctionType::Representation::CFunctionPointer || - rep == FunctionType::Representation::Block) { - diagnose(attrs.getLoc(TAK_convention), - diag::invalid_autoclosure_and_convention_attributes, - attrs.getConventionName()); - attrs.clearAttribute(TAK_convention); - } - } - } - } - // @autoclosure is only valid on parameters. - if (!isParam && attrs.has(TAK_autoclosure)) { - bool isVariadicFunctionParam = - options.is(TypeResolverContext::VariadicFunctionInput) && - !options.hasBase(TypeResolverContext::EnumElementDecl); - - diagnose(attrs.getLoc(TAK_autoclosure), - isVariadicFunctionParam ? diag::attr_not_on_variadic_parameters - : diag::attr_only_on_parameters, - "@autoclosure"); - attrs.clearAttribute(TAK_autoclosure); + bool isCOrBlock = rep == FunctionTypeRepresentation::CFunctionPointer + || rep == FunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); + } } if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_differentiable), - diag::experimental_differentiable_programming_disabled); + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; @@ -2281,12 +2314,43 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } ty = resolveASTFunctionType(fnRepr, options, rep, /*noescape=*/false, + parsedClangFunctionType, diffKind); if (!ty || ty->hasError()) return ty; } } + // Validate use of @autoclosure + if (attrs.has(TAK_autoclosure)) { + bool didDiagnose = false; + if (attrs.hasConvention()) { + if (attrs.getConventionName() == "c" || + attrs.getConventionName() == "block") { + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::invalid_autoclosure_and_convention_attributes, + attrs.getConventionName()); + attrs.clearAttribute(TAK_convention); + didDiagnose = true; + } + } else if (options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl)) { + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_not_on_variadic_parameters, "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; + } else if (!options.is(TypeResolverContext::FunctionInput)) { + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_only_on_parameters, "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; + } + + if (didDiagnose) { + ty = ErrorType::get(Context); + } + } + auto instanceOptions = options; instanceOptions.setContext(None); @@ -2312,12 +2376,13 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto loc = attrs.getLoc(TAK_escaping); auto attrRange = getTypeAttrRangeWithAt(Context, loc); - diagnose(loc, diag::escaping_non_function_parameter) - .fixItRemove(attrRange); + diagnoseInvalid(repr, loc, diag::escaping_non_function_parameter) + .fixItRemove(attrRange); // Try to find a helpful note based on how the type is being used if (options.is(TypeResolverContext::ImmediateOptionalTypeArgument)) { - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } @@ -2333,6 +2398,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // @autoclosure is going to be diagnosed when type of // the parameter is validated, because that attribute // applies to the declaration now. + repr->setInvalid(); attrs.clearAttribute(TAK_autoclosure); } @@ -2340,9 +2406,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (!attrs.has(i)) continue; - auto diag = diagnose(attrs.getLoc(i), - diag::attribute_requires_function_type, - TypeAttributes::getAttrName(i)); + auto diag = diagnoseInvalid(repr, attrs.getLoc(i), + diag::attribute_requires_function_type, + TypeAttributes::getAttrName(i)); // If we see @escaping among the attributes on this type, because it isn't // a function type, we'll remove it. @@ -2352,7 +2418,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Specialize the diagnostic for Optionals. if (ty->getOptionalObjectType()) { diag.flush(); - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } attrs.clearAttribute(i); @@ -2364,10 +2431,26 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, attrs.ConventionArguments = None; } + if (attrs.has(TAK_noDerivative)) { + if (!Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnose(attrs.getLoc(TAK_noDerivative), + diag::experimental_differentiable_programming_disabled); + } else if (!isParam) { + // @noDerivative is only valid on parameters. + diagnose(attrs.getLoc(TAK_noDerivative), + (isVariadicFunctionParam + ? diag::attr_not_on_variadic_parameters + : diag::attr_only_on_parameters_of_differentiable), + "@noDerivative"); + } + attrs.clearAttribute(TAK_noDerivative); + } + // In SIL, handle @opened (n), which creates an existential archetype. if (attrs.has(TAK_opened)) { if (!ty->isExistentialType()) { - diagnose(attrs.getLoc(TAK_opened), diag::opened_non_protocol, ty); + diagnoseInvalid(repr, attrs.getLoc(TAK_opened), diag::opened_non_protocol, + ty); } else { ty = OpenedArchetypeType::get(ty, attrs.OpenedID); } @@ -2407,16 +2490,17 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } for (unsigned i = 0; i != TypeAttrKind::TAK_Count; ++i) - if (attrs.has((TypeAttrKind)i)) - diagnose(attrs.getLoc((TypeAttrKind)i), - diag::attribute_does_not_apply_to_type); + if (attrs.has((TypeAttrKind)i)) { + diagnoseInvalid(repr, attrs.getLoc((TypeAttrKind)i), + diag::attribute_does_not_apply_to_type); + } return ty; } bool TypeResolver::resolveASTFunctionTypeParams( TupleTypeRepr *inputRepr, TypeResolutionOptions options, - bool requiresMappingOut, + bool requiresMappingOut, DifferentiabilityKind diffKind, SmallVectorImpl &elements) { elements.reserve(inputRepr->getNumElements()); @@ -2480,8 +2564,24 @@ bool TypeResolver::resolveASTFunctionTypeParams( ownership = ValueOwnership::Default; break; } + + bool noDerivative = false; + if (auto *attrTypeRepr = dyn_cast(eltTypeRepr)) { + if (attrTypeRepr->getAttrs().has(TAK_noDerivative)) { + if (diffKind == DifferentiabilityKind::NonDifferentiable && + Context.LangOpts.EnableExperimentalDifferentiableProgramming) + diagnose(eltTypeRepr->getLoc(), + diag::attr_only_on_parameters_of_differentiable, + "@noDerivative") + .highlight(eltTypeRepr->getSourceRange()); + else + noDerivative = true; + } + } + auto paramFlags = ParameterTypeFlags::fromParameterType( - ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership); + ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership, + noDerivative); elements.emplace_back(ty, Identifier(), paramFlags); } @@ -2528,6 +2628,7 @@ Type TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, Type TypeResolver::resolveASTFunctionType( FunctionTypeRepr *repr, TypeResolutionOptions parentOptions, AnyFunctionType::Representation representation, bool noescape, + const clang::Type *parsedClangFunctionType, DifferentiabilityKind diffKind) { TypeResolutionOptions options = None; @@ -2535,7 +2636,8 @@ Type TypeResolver::resolveASTFunctionType( SmallVector params; if (resolveASTFunctionTypeParams(repr->getArgsTypeRepr(), options, - repr->getGenericEnvironment() != nullptr, params)) { + repr->getGenericEnvironment() != nullptr, + diffKind, params)) { return Type(); } @@ -2565,8 +2667,9 @@ Type TypeResolver::resolveASTFunctionType( noescape, repr->throws(), diffKind, /*clangFunctionType*/nullptr); - const clang::Type *clangFnType = nullptr; - if (representation == AnyFunctionType::Representation::CFunctionPointer) + const clang::Type *clangFnType = parsedClangFunctionType; + if (representation == AnyFunctionType::Representation::CFunctionPointer + && !clangFnType) clangFnType = Context.getClangFunctionType( params, outputTy, incompleteExtInfo, AnyFunctionType::Representation::CFunctionPointer); @@ -3032,8 +3135,7 @@ Type TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr, default: llvm_unreachable("unknown SpecifierTypeRepr kind"); } - diagnose(repr->getSpecifierLoc(), diagID, name); - repr->setInvalid(); + diagnoseInvalid(repr, repr->getSpecifierLoc(), diagID, name); return ErrorType::get(Context); } diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index e9c5c3162ba4e..bcd564c9b20ba 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -66,6 +66,9 @@ enum class TypeResolutionFlags : uint16_t { /// Whether we should not produce diagnostics if the type is invalid. SilenceErrors = 1 << 10, + + /// Whether to allow module declaration types. + AllowModule = 1 << 11 }; /// Type resolution contexts that require special handling. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 4918500c12e5f..b5a702309c7f3 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -263,7 +263,7 @@ static void bindExtensions(SourceFile &SF) { if (!SF) continue; - for (auto D : SF->Decls) { + for (auto D : SF->getTopLevelDecls()) { if (auto ED = dyn_cast(D)) if (!tryBindExtension(ED)) worklist.push_back(ED); @@ -378,7 +378,7 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, ::bindExtensions(*SF); // Type check the top-level elements of the source file. - for (auto D : llvm::makeArrayRef(SF->Decls).slice(StartElem)) { + for (auto D : SF->getTopLevelDecls().slice(StartElem)) { if (auto *TLCD = dyn_cast(D)) { // Immediately perform global name-binding etc. TypeChecker::typeCheckTopLevelCodeDecl(TLCD); @@ -465,7 +465,7 @@ void swift::checkInconsistentImplementationOnlyImports(ModuleDecl *MainModule) { if (!SF) continue; - for (auto *topLevelDecl : SF->Decls) { + for (auto *topLevelDecl : SF->getTopLevelDecls()) { auto *nextImport = dyn_cast(topLevelDecl); if (!nextImport) continue; @@ -575,6 +575,8 @@ void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, auto &Ctx = PBD->getASTContext(); DiagnosticSuppression suppression(Ctx.Diags); (void)createTypeChecker(Ctx); + (void)evaluateOrDefault( + Ctx.evaluator, PatternBindingEntryRequest{PBD, bindingIndex}, nullptr); TypeChecker::typeCheckPatternBinding(PBD, bindingIndex); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 0de240b468473..57a2f852daa1e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -182,10 +182,6 @@ enum class TypeCheckExprFlags { /// not affect type checking itself. IsExprStmt = 0x20, - /// If set, this expression is being re-type checked as part of diagnostics, - /// and so we should not visit bodies of non-single expression closures. - SkipMultiStmtClosures = 0x40, - /// This is an inout yield. IsInOutYield = 0x100, @@ -704,9 +700,16 @@ class TypeChecker final { SourceLoc EndTypeCheckLoc); static bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD); - static BraceStmt *applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType); + /// Try to apply the function builder transform of the given builder type + /// to the body of the function. + /// + /// \returns \c None if the builder transformation cannot be applied at all, + /// e.g., because of a \c return statement. Otherwise, returns either the + /// fully type-checked body of the function (on success) or a \c nullptr + /// value if an error occurred while type checking the transformed body. + static Optional applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType); + static bool typeCheckClosureBody(ClosureExpr *closure); static bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); @@ -1003,27 +1006,24 @@ class TypeChecker final { /// Type check the given pattern. /// - /// \param P The pattern to type check. - /// \param dc The context in which type checking occurs. - /// \param options Options that control type resolution. - /// - /// \returns true if any errors occurred during type checking. - static bool typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options); + /// \returns the type of the pattern, which may be an error type if an + /// unrecoverable error occurred. If the options permit it, the type may + /// involve \c UnresolvedType (for patterns with no type information) and + /// unbound generic types. + static Type typeCheckPattern(ContextualPattern pattern); static bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc); /// Coerce a pattern to the given type. /// - /// \param P The pattern, which may be modified by this coercion. - /// \param resolution The type resolution. + /// \param pattern The contextual pattern. /// \param type the type to coerce the pattern to. - /// \param options Options describing how to perform this coercion. + /// \param options Options that control the coercion. /// - /// \returns true if an error occurred, false otherwise. - static bool coercePatternToType(Pattern *&P, TypeResolution resolution, Type type, - TypeResolutionOptions options, - TypeLoc tyLoc = TypeLoc()); + /// \returns the coerced pattern, or nullptr if the coercion failed. + static Pattern *coercePatternToType(ContextualPattern pattern, Type type, + TypeResolutionOptions options, + TypeLoc tyLoc = TypeLoc()); static bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type type); @@ -1033,10 +1033,15 @@ class TypeChecker final { AnyFunctionType *FN); /// Type-check an initialized variable pattern declaration. - static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC); - static bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber); + static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC, + Type patternType); + static bool typeCheckPatternBinding(PatternBindingDecl *PBD, + unsigned patternNumber, + Type patternType = Type()); /// Type-check a for-each loop's pattern binding and sequence together. + /// + /// \returns true if a failure occurred. static bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt); /// Compute the set of captures for the given function or closure. @@ -1105,17 +1110,6 @@ class TypeChecker final { /// this protocol. static Type getDefaultType(ProtocolDecl *protocol, DeclContext *dc); - /// Convert the given expression to the given type. - /// - /// \param expr The expression, which will be updated in place. - /// \param type The type to convert to. - /// \param typeFromPattern Optionally, the caller can specify the pattern - /// from where the toType is derived, so that we can deliver better fixit. - /// - /// \returns true if an error occurred, false otherwise. - static bool convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern = None); - /// Coerce the given expression to materializable type, if it /// isn't already. static Expr *coerceToRValue( @@ -1593,18 +1587,19 @@ class TypeChecker final { static DeclTypeCheckingSemantics getDeclTypeCheckingSemantics(ValueDecl *decl); - /// Creates an `IndexSubset` for the given function type, representing - /// all inferred differentiation parameters. Used by `@differentiable` and - /// `@derivative` attribute type-checking. + /// Infers the differentiability parameter indices for the given + /// original or derivative `AbstractFunctionDecl`. + /// + /// The differentiability parameters are inferred to be: + /// - All parameters of the function that conform to `Differentiable`. + /// - If the function result type is a function type (i.e. the function has + /// a curried method type), then also all parameters of the function result + /// type that conform to `Differentiable`. /// - /// The differentiation parameters are inferred to be: - /// - All parameters of the function type that conform to `Differentiable`. - /// - If the function type's result is a function type (i.e. it is a curried - /// method type), then also all parameters of the function result type that - /// conform to `Differentiable`. + /// Used by `@differentiable` and `@derivative` attribute type-checking. static IndexSubset * - inferDifferentiationParameters(AbstractFunctionDecl *AFD, - GenericEnvironment *derivativeGenEnv); + inferDifferentiabilityParameters(AbstractFunctionDecl *AFD, + GenericEnvironment *derivativeGenEnv); public: /// Require that the library intrinsics for working with Optional diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 31646a5219c2a..83ae9830bd143 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1889,14 +1889,25 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { } DeclContext *ModuleFile::getDeclContext(DeclContextID DCID) { + auto deserialized = getDeclContextChecked(DCID); + if (!deserialized) { + fatal(deserialized.takeError()); + } + return deserialized.get(); +} + +Expected ModuleFile::getDeclContextChecked(DeclContextID DCID) { if (!DCID) return FileContext; if (Optional contextID = DCID.getAsLocalDeclContextID()) return getLocalDeclContext(contextID.getValue()); - auto D = getDecl(DCID.getAsDeclID().getValue()); + auto deserialized = getDeclChecked(DCID.getAsDeclID().getValue()); + if (!deserialized) + return deserialized.takeError(); + auto D = deserialized.get(); if (auto GTD = dyn_cast(D)) return GTD; if (auto ED = dyn_cast(D)) @@ -2221,7 +2232,8 @@ Decl *ModuleFile::getDecl(DeclID DID) { } /// Used to split up methods that would otherwise live in ModuleFile. -class swift::DeclDeserializer { +namespace swift { +class DeclDeserializer { template using Serialized = ModuleFile::Serialized; using TypeID = serialization::TypeID; @@ -3415,6 +3427,7 @@ class swift::DeclDeserializer { DeclContextID contextID; bool isImplicit, isObjC; bool inheritsSuperclassInitializers; + bool hasMissingDesignatedInits; GenericSignatureID genericSigID; TypeID superclassID; uint8_t rawAccessLevel; @@ -3423,6 +3436,7 @@ class swift::DeclDeserializer { decls_block::ClassLayout::readRecord(scratch, nameID, contextID, isImplicit, isObjC, inheritsSuperclassInitializers, + hasMissingDesignatedInits, genericSigID, superclassID, rawAccessLevel, numConformances, numInheritedTypes, @@ -3465,6 +3479,8 @@ class swift::DeclDeserializer { theClass->setSuperclass(MF.getType(superclassID)); ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass}, std::move(inheritsSuperclassInitializers)); + ctx.evaluator.cacheOutput(HasMissingDesignatedInitializersRequest{theClass}, + std::move(hasMissingDesignatedInits)); handleInherited(theClass, rawInheritedAndDependencyIDs.slice(0, numInheritedTypes)); @@ -3496,7 +3512,6 @@ class swift::DeclDeserializer { numConformances, numInherited, rawInheritedAndDependencyIDs); - auto DC = MF.getDeclContext(contextID); if (declOrOffset.isComplete()) return declOrOffset; @@ -3510,6 +3525,11 @@ class swift::DeclDeserializer { } } + auto DCOrError = MF.getDeclContextChecked(contextID); + if (!DCOrError) + return DCOrError.takeError(); + auto DC = DCOrError.get(); + auto genericParams = MF.maybeReadGenericParams(DC); if (declOrOffset.isComplete()) return declOrOffset; @@ -3873,6 +3893,7 @@ class swift::DeclDeserializer { return dtor; } }; +} Expected ModuleFile::getDeclChecked( @@ -4202,11 +4223,12 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { parametersBitVector[i] = parameters[i]; auto *indices = IndexSubset::get(ctx, parametersBitVector); - auto *derivAttr = DerivativeAttr::create( - ctx, isImplicit, SourceLoc(), SourceRange(), origName, indices); - derivAttr->setOriginalFunction(origDecl); - derivAttr->setDerivativeKind(*derivativeKind); - Attr = derivAttr; + auto *derivativeAttr = + DerivativeAttr::create(ctx, isImplicit, SourceLoc(), SourceRange(), + /*baseType*/ nullptr, origName, indices); + derivativeAttr->setOriginalFunction(origDecl); + derivativeAttr->setDerivativeKind(*derivativeKind); + Attr = derivativeAttr; break; } @@ -4512,7 +4534,8 @@ Type ModuleFile::getType(TypeID TID) { return deserialized.get(); } -class swift::TypeDeserializer { +namespace swift { +class TypeDeserializer { using TypeID = serialization::TypeID; ModuleFile &MF; @@ -4770,12 +4793,11 @@ class swift::TypeDeserializer { IdentifierID labelID; TypeID typeID; - bool isVariadic, isAutoClosure, isNonEphemeral; + bool isVariadic, isAutoClosure, isNonEphemeral, isNoDerivative; unsigned rawOwnership; - decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID, - isVariadic, isAutoClosure, - isNonEphemeral, - rawOwnership); + decls_block::FunctionParamLayout::readRecord( + scratch, labelID, typeID, isVariadic, isAutoClosure, isNonEphemeral, + rawOwnership, isNoDerivative); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -4786,10 +4808,10 @@ class swift::TypeDeserializer { if (!paramTy) return paramTy.takeError(); - params.emplace_back(paramTy.get(), - MF.getIdentifier(labelID), + params.emplace_back(paramTy.get(), MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, - isNonEphemeral, *ownership)); + isNonEphemeral, *ownership, + isNoDerivative)); } if (!isGeneric) { @@ -5290,6 +5312,7 @@ class swift::TypeDeserializer { return UnboundGenericType::get(genericDecl, parentTy, ctx); } }; +} Expected ModuleFile::getTypeChecked(TypeID TID) { if (TID == 0) @@ -5386,7 +5409,8 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context, Decl *suppliedMissingMember = nullptr; auto handleMissingClassMember = [&](const DeclDeserializationError &error) { if (error.isDesignatedInitializer()) - containingClass->setHasMissingDesignatedInitializers(); + context.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{containingClass}, true); if (error.getNumberOfVTableEntries() > 0) containingClass->setHasMissingVTableEntries(); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 008fcf1eab36e..eb97ef6d9c322 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1429,7 +1429,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // two values in the list are the basic block identifiers. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILBasicBlock *errorBB = getBBForReference(Fn, ListOfValues.back()); @@ -1455,7 +1455,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::PartialApplyInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); SILType closureTy = getSILType(Ty2, SILValueCategory::Object, Fn); SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index aad0ccc278fbc..510a62db0c63b 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -1983,7 +1983,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, auto scopeID = ctx.getIdentifier(scopePath); assert(!scopeID.empty() && "invalid decl name (non-top-level decls not supported)"); - std::pair accessPathElem(scopeID, SourceLoc()); + Located accessPathElem = { scopeID, SourceLoc() }; dependency.Import = {ctx.AllocateCopy(llvm::makeArrayRef(accessPathElem)), module}; } @@ -2202,14 +2202,14 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { if (Dep.isScoped()) std::tie(ModulePathStr, ScopePath) = ModulePathStr.rsplit('\0'); - SmallVector, 1> AccessPath; + SmallVector, 1> AccessPath; while (!ModulePathStr.empty()) { StringRef NextComponent; std::tie(NextComponent, ModulePathStr) = ModulePathStr.split('\0'); AccessPath.push_back({Ctx.getIdentifier(NextComponent), SourceLoc()}); } - if (AccessPath.size() == 1 && AccessPath[0].first == Ctx.StdlibModuleName) + if (AccessPath.size() == 1 && AccessPath[0].Item == Ctx.StdlibModuleName) continue; ModuleDecl *M = Ctx.getLoadedModule(AccessPath); @@ -2228,7 +2228,7 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { // Lookup the decl in the top-level module. ModuleDecl *TopLevelModule = M; if (AccessPath.size() > 1) - TopLevelModule = Ctx.getLoadedModule(AccessPath.front().first); + TopLevelModule = Ctx.getLoadedModule(AccessPath.front().Item); SmallVector Decls; TopLevelModule->lookupQualified( @@ -2278,7 +2278,7 @@ void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, }; if (!accessPath.empty()) { - auto iter = TopLevelDecls->find(accessPath.front().first); + auto iter = TopLevelDecls->find(accessPath.front().Item); if (iter == TopLevelDecls->end()) return; @@ -2454,7 +2454,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } else { @@ -2467,7 +2467,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } @@ -2496,7 +2496,7 @@ void ModuleFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index bc1110599077a..2ae1a88a3da06 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -913,6 +913,11 @@ class ModuleFile /// Returns the decl context with the given ID, deserializing it if needed. DeclContext *getDeclContext(serialization::DeclContextID DID); + /// Returns the decl context with the given ID, deserializing it if needed, + /// or the first error. + llvm::Expected + getDeclContextChecked(serialization::DeclContextID DCID); + /// Returns the local decl context with the given ID, deserializing it if needed. DeclContext *getLocalDeclContext(serialization::LocalDeclContextID DID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 75f5f99815c3c..601a1c0593f09 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 530; // @_implicitly_synthesizes_nested_requirement +const uint16_t SWIFTMODULE_VERSION_MINOR = 532; // @_hasMissingDesignatedInitializers /// A standard hash seed used for all string hashes in a serialized module. /// @@ -905,12 +905,13 @@ namespace decls_block { using FunctionParamLayout = BCRecordLayout< FUNCTION_PARAM, - IdentifierIDField, // name - TypeIDField, // type - BCFixed<1>, // vararg? - BCFixed<1>, // autoclosure? - BCFixed<1>, // non-ephemeral? - ValueOwnershipField // inout, shared or owned? + IdentifierIDField, // name + TypeIDField, // type + BCFixed<1>, // vararg? + BCFixed<1>, // autoclosure? + BCFixed<1>, // non-ephemeral? + ValueOwnershipField, // inout, shared or owned? + BCFixed<1> // noDerivative? >; using MetatypeTypeLayout = BCRecordLayout< @@ -1113,6 +1114,7 @@ namespace decls_block { BCFixed<1>, // implicit? BCFixed<1>, // explicitly objc? BCFixed<1>, // inherits convenience initializers from its superclass? + BCFixed<1>, // has missing designated initializers? GenericSignatureIDField, // generic environment TypeIDField, // superclass AccessLevelField, // access level @@ -1790,6 +1792,14 @@ namespace decls_block { BCArray> // Differentiation parameter indices' bitvector. >; + using TransposeDeclAttrLayout = BCRecordLayout< + Transpose_DECL_ATTR, + BCFixed<1>, // Implicit flag. + IdentifierIDField, // Original name. + DeclIDField, // Original function declaration. + BCArray> // Transposed parameter indices' bitvector. + >; + #define SIMPLE_DECL_ATTR(X, CLASS, ...) \ using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 78fbb5d7762b5..5ca585f982197 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -915,7 +915,7 @@ static void flattenImportPath(const ModuleDecl::ImportedModule &import, outStream << '\0'; assert(import.first.size() == 1 && "can only handle top-level decl imports"); auto accessPathElem = import.first.front(); - outStream << accessPathElem.first.str(); + outStream << accessPathElem.Item.str(); } uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { @@ -2003,7 +2003,8 @@ static uint8_t getRawStableAutoDiffDerivativeFunctionKind( swift::AutoDiffDerivativeFunctionKind kind) { switch (kind) { case swift::AutoDiffDerivativeFunctionKind::JVP: - return uint8_t(serialization::AutoDiffDerivativeFunctionKind::JVP); case swift::AutoDiffDerivativeFunctionKind::VJP: + return uint8_t(serialization::AutoDiffDerivativeFunctionKind::JVP); + case swift::AutoDiffDerivativeFunctionKind::VJP: return uint8_t(serialization::AutoDiffDerivativeFunctionKind::VJP); } llvm_unreachable("bad derivative function kind"); @@ -2360,6 +2361,31 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_Transpose: { + auto abbrCode = S.DeclTypeAbbrCodes[TransposeDeclAttrLayout::Code]; + auto *attr = cast(DA); + // NOTE(TF-838): `@transpose` attribute serialization is blocked by + // `@transpose` attribute type-checking (TF-830), which resolves + // the original declaration. + if (!attr->getOriginalFunction()) + return; + assert(attr->getOriginalFunction() && + "`@transpose` attribute should have original declaration set " + "during construction or parsing"); + auto origName = attr->getOriginalFunctionName().Name.getBaseName(); + IdentifierID origNameId = S.addDeclBaseNameRef(origName); + DeclID origDeclID = S.addDeclRef(attr->getOriginalFunction()); + auto *parameterIndices = attr->getParameterIndices(); + assert(parameterIndices && "Parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(parameterIndices->getCapacity())) + indices.push_back(parameterIndices->contains(i)); + TransposeDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), origNameId, + origDeclID, indices); + return; + } + case DAK_ImplicitlySynthesizesNestedRequirement: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[ImplicitlySynthesizesNestedRequirementDeclAttrLayout::Code]; @@ -3113,9 +3139,7 @@ class Serializer::DeclSerializer : public DeclVisitor { uint8_t rawAccessLevel = getRawStableAccessLevel(theClass->getFormalAccess()); - bool inheritsSuperclassInitializers = - const_cast(theClass)-> - inheritsSuperclassInitializers(); + auto mutableClass = const_cast(theClass); unsigned abbrCode = S.DeclTypeAbbrCodes[ClassLayout::Code]; ClassLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, @@ -3123,7 +3147,8 @@ class Serializer::DeclSerializer : public DeclVisitor { contextID.getOpaqueValue(), theClass->isImplicit(), theClass->isObjC(), - inheritsSuperclassInitializers, + mutableClass->inheritsSuperclassInitializers(), + mutableClass->hasMissingDesignatedInitializers(), S.addGenericSignatureRef( theClass->getGenericSignature()), S.addTypeRef(theClass->getSuperclass()), @@ -3995,8 +4020,8 @@ class Serializer::TypeSerializer : public TypeVisitor { S.Out, S.ScratchRecord, abbrCode, S.addDeclBaseNameRef(param.getLabel()), S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(), - paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), - rawOwnership); + paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), rawOwnership, + paramFlags.isNoDerivative()); } } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 903391055dd75..bee83dc0e7a7a 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -428,7 +428,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, bool &isFramework, bool &isSystemModule) { - llvm::SmallString<64> moduleName(moduleID.first.str()); + llvm::SmallString<64> moduleName(moduleID.Item.str()); ModuleFilenamePair fileNames(moduleName); SmallVector targetFileNamePairs; @@ -465,7 +465,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // We can only get here if all targetFileNamePairs failed with // 'std::errc::no_such_file_or_directory'. - if (maybeDiagnoseTargetMismatch(moduleID.second, moduleName, + if (maybeDiagnoseTargetMismatch(moduleID.Loc, moduleName, primaryTargetSpecificName, currPath)) { return false; } else { @@ -517,7 +517,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, case SearchPathKind::Framework: { isFramework = true; llvm::sys::path::append(currPath, - moduleID.first.str() + ".framework"); + moduleID.Item.str() + ".framework"); // Check if the framework directory exists. if (!fs.exists(currPath)) @@ -747,7 +747,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( auto circularDependencyIter = llvm::find_if(loadedModuleFile->getDependencies(), [](const ModuleFile::Dependency &next) { - return !next.Import.second->hasResolvedImports(); + return next.isLoaded() && + !next.Import.second->hasResolvedImports(); }); assert(circularDependencyIter != loadedModuleFile->getDependencies().end() && @@ -824,7 +825,7 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( } bool SerializedModuleLoaderBase::canImportModule( - std::pair mID) { + Located mID) { // Look on disk. SmallVector *unusedModuleInterfacePath = nullptr; std::unique_ptr *unusedModuleBuffer = nullptr; @@ -838,9 +839,9 @@ bool SerializedModuleLoaderBase::canImportModule( } bool MemoryBufferSerializedModuleLoader::canImportModule( - std::pair mID) { + Located mID) { // See if we find it in the registered memory buffers. - return MemoryBuffers.count(mID.first.str()); + return MemoryBuffers.count(mID.Item.str()); } ModuleDecl * @@ -875,15 +876,15 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, assert(moduleInputBuffer); - auto M = ModuleDecl::create(moduleID.first, Ctx); + auto M = ModuleDecl::create(moduleID.Item, Ctx); M->setIsSystemModule(isSystemModule); - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.Item] = M; SWIFT_DEFER { M->setHasResolvedImports(); }; StringRef moduleInterfacePathStr = Ctx.AllocateCopy(moduleInterfacePath.str()); - if (!loadAST(*M, moduleID.second, moduleInterfacePathStr, + if (!loadAST(*M, moduleID.Loc, moduleInterfacePathStr, std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, /*treatAsPartialModule*/false)) { @@ -907,7 +908,7 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, // FIXME: Right now this works only with access paths of length 1. // Once submodules are designed, this needs to support suffix // matching and a search path. - auto bufIter = MemoryBuffers.find(moduleID.first.str()); + auto bufIter = MemoryBuffers.find(moduleID.Item.str()); if (bufIter == MemoryBuffers.end()) return nullptr; @@ -918,16 +919,16 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, MemoryBuffers.erase(bufIter); assert(moduleInputBuffer); - auto *M = ModuleDecl::create(moduleID.first, Ctx); + auto *M = ModuleDecl::create(moduleID.Item, Ctx); SWIFT_DEFER { M->setHasResolvedImports(); }; - if (!loadAST(*M, moduleID.second, /*moduleInterfacePath*/ "", + if (!loadAST(*M, moduleID.Loc, /*moduleInterfacePath*/ "", std::move(moduleInputBuffer), {}, {}, isFramework, treatAsPartialModule)) { return nullptr; } - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.Item] = M; return M; } diff --git a/lib/SymbolGraphGen/CMakeLists.txt b/lib/SymbolGraphGen/CMakeLists.txt new file mode 100644 index 0000000000000..2780330587fb2 --- /dev/null +++ b/lib/SymbolGraphGen/CMakeLists.txt @@ -0,0 +1,13 @@ +add_swift_host_library(swiftSymbolGraphGen STATIC + DeclarationFragmentPrinter.cpp + Edge.cpp + JSON.cpp + Symbol.cpp + SymbolGraph.cpp + SymbolGraphGen.cpp + SymbolGraphASTWalker.cpp) + +target_link_libraries(swiftSymbolGraphGen + swiftAST + swiftFrontend + swiftMarkup) diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp new file mode 100644 index 0000000000000..2e8db85e2d3b2 --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp @@ -0,0 +1,140 @@ +//===--- DeclarationFragmentPrinter.cpp - Declaration Fragment Printer ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void DeclarationFragmentPrinter::openFragment(FragmentKind Kind) { + assert(Kind != FragmentKind::None); + if (this->Kind != Kind) { + closeFragment(); + this->Kind = Kind, + Spelling.clear(); + USR.clear(); + } +} + +StringRef +DeclarationFragmentPrinter::getKindSpelling(FragmentKind Kind) const { + switch (Kind) { + case FragmentKind::Keyword: + return "keyword"; + case FragmentKind::Attribute: + return "attribute"; + case FragmentKind::NumberLiteral: + return "number"; + case FragmentKind::StringLiteral: + return "string"; + case FragmentKind::Identifier: + return "identifier"; + case FragmentKind::TypeIdentifier: + return "typeIdentifier"; + case FragmentKind::GenericParameter: + return "genericParameter"; + case FragmentKind::Text: + return "text"; + case FragmentKind::None: + llvm_unreachable("Fragment kind of 'None' has no spelling"); + } +} + +void DeclarationFragmentPrinter::closeFragment() { + if (Kind == FragmentKind::None) { + return; + } + + if (!Spelling.empty()) { + OS.object([&](){ + OS.attribute("kind", getKindSpelling(Kind)); + OS.attribute("spelling", Spelling.str()); + if (!USR.empty()) { + OS.attribute("preciseIdentifier", USR.str()); + } + }); + } + + Spelling.clear(); + USR.clear(); + Kind = FragmentKind::None; +} + +void DeclarationFragmentPrinter::printDeclLoc(const Decl *D) { + switch (D->getKind()) { + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Subscript: + openFragment(FragmentKind::Keyword); + break; + default: + openFragment(FragmentKind::Identifier); + break; + } +} + +void +DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) { + switch (Context) { + case PrintNameContext::Keyword: + openFragment(FragmentKind::Keyword); + break; + case PrintNameContext::GenericParameter: + openFragment(FragmentKind::GenericParameter); + break; + case PrintNameContext::Attribute: + openFragment(FragmentKind::Attribute); + break; + case PrintNameContext::ClassDynamicSelf: + case PrintNameContext::FunctionParameterExternal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::FunctionParameterLocal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::TupleElement: + case PrintNameContext::TypeMember: + case PrintNameContext::Normal: + break; + } +} + +void DeclarationFragmentPrinter::printStructurePre(PrintStructureKind Kind, + const Decl *D) { + switch (Kind) { + case PrintStructureKind::NumberLiteral: + openFragment(FragmentKind::NumberLiteral); + break; + case PrintStructureKind::StringLiteral: + openFragment(FragmentKind::StringLiteral); + break; + default: + break; + } +} + +void DeclarationFragmentPrinter::printTypeRef(Type T, const TypeDecl *RefTo, + Identifier Name, + PrintNameContext NameContext) { + openFragment(FragmentKind::TypeIdentifier); + printText(Name.str()); + USR = Walker.getUSR(RefTo); + closeFragment(); +} + +void DeclarationFragmentPrinter::printText(StringRef Text) { + if (Kind == FragmentKind::None) { + openFragment(FragmentKind::Text); + } + Spelling.append(Text); +} diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.h b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h new file mode 100644 index 0000000000000..2e5ec449bd34f --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h @@ -0,0 +1,123 @@ +//===--- DeclarationFragmentPrinter.h - Declaration Fragment Printer ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H +#define SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/Basic/LLVM.h" + +namespace swift { + +class Decl; +class Type; +class TypeDecl; + +namespace symbolgraphgen { + +struct SymbolGraphASTWalker; + +/// Prints AST nodes as a stream of tagged fragments for syntax highlighting. +/// +/// These fragments are meant to display a somewhat abbreviated part of the +/// declaration for display in documentation, ignoring things like member and +/// function bodies. +/// +/// For example, a function: +/// +/// ```swift +/// func foo() { +/// print("Hello, world!") +/// } +/// ``` +/// +/// Will have fragments representing the `func foo()` part. +class DeclarationFragmentPrinter : public ASTPrinter { + enum class FragmentKind { + None, + Keyword, + Attribute, + NumberLiteral, + StringLiteral, + Identifier, + TypeIdentifier, + GenericParameter, + Text, + }; + + SymbolGraphASTWalker &Walker; + + /// The output stream to print fragment objects to. + llvm::json::OStream &OS; + + /// The current fragment being considered. + FragmentKind Kind; + + /// The actual source text of the fragment. + SmallString<256> Spelling; + + SmallString<256> USR; + + StringRef getKindSpelling(FragmentKind Kind) const; + + /// Open a new kind of fragment without committing its spelling. + void openFragment(FragmentKind Kind); + + /// Close the current fragment if there is one, and commit it for display. + void closeFragment(); + +public: + DeclarationFragmentPrinter(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS, + Optional Key = None) + : Walker(Walker), + OS(OS), + Kind(FragmentKind::None) { + if (Key) { + OS.attributeBegin(*Key); + OS.arrayBegin(); + } else { + OS.arrayBegin(); + } + } + + void printDeclLoc(const Decl *D) override; + + void printDeclNameEndLoc(const Decl *D) override { + closeFragment(); + } + + void printNamePre(PrintNameContext Context) override; + + void printStructurePre(PrintStructureKind Kind, const Decl *D) override; + + void printNamePost(PrintNameContext Context) override { + closeFragment(); + } + + void printTypeRef(Type T, const TypeDecl *RefTo, Identifier Name, + PrintNameContext NameContext) override; + + void printText(StringRef Text) override; + + ~DeclarationFragmentPrinter() { + closeFragment(); + OS.arrayEnd(); + OS.attributeEnd(); + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp new file mode 100644 index 0000000000000..296cc0960fee2 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.cpp @@ -0,0 +1,40 @@ +//===--- Edge.cpp - Symbol Graph Edge -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "Edge.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Edge::serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("kind", Kind.Name); + OS.attribute("source", Walker->getUSR(Source)); + OS.attribute("target", Walker->getUSR(Target)); + + // In case a dependent module isn't available, serialize a fallback name. + auto TargetModuleName = Target->getModuleContext()->getName().str(); + if (TargetModuleName != Walker->M.getName().str()) { + auto TargetSymbolIdentifier = Walker->getSymbolIdentifier(Target); + auto TargetComponents = TargetSymbolIdentifier.SimpleComponents; + SmallString<128> Scratch(TargetModuleName); + for (auto it = TargetComponents.begin(); + it != TargetComponents.end(); ++it) { + Scratch.push_back('.'); + Scratch.append(*it); + } + OS.attribute("targetFallback", Scratch.str()); + } + }); +} diff --git a/lib/SymbolGraphGen/Edge.h b/lib/SymbolGraphGen/Edge.h new file mode 100644 index 0000000000000..e22d2880151a7 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.h @@ -0,0 +1,167 @@ +//===--- Edge.h - Symbol Graph Edge ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_EDGE_H +#define SWIFT_SYMBOLGRAPHGEN_EDGE_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Decl.h" +#include "swift/Basic/LLVM.h" + +#include "JSON.h" +#include "Symbol.h" + +namespace swift { +namespace symbolgraphgen { + +/// The kind of relationship, tagging an edge in the graph. +struct RelationshipKind { + StringRef Name; + + RelationshipKind(llvm::StringRef Name) : Name(Name) {} + + /** + A symbol A is a member of another symbol B. + + For example, a method or field of a class would be a member of that class. + + The implied inverse of this relationship is a symbol B is the owner + of a member symbol A. + */ + static inline RelationshipKind MemberOf() { + return RelationshipKind { "memberOf" }; + } + + /** + A symbol A conforms to an interface/protocol symbol B. + + For example, a class `C` that conforms to protocol `P` in Swift would use + this relationship. + + The implied inverse of this relationship is a symbol B that has + a conformer A. + */ + static inline RelationshipKind ConformsTo() { + return RelationshipKind { "conformsTo" }; + } + /** + A symbol A inherits from another symbol B. + + For example, a derived class inherits from a base class, or a protocol + that refines another protocol would use this relationship. + + The implied inverse of this relationship is a symbol B is a base + of another symbol A. + */ + static inline RelationshipKind InheritsFrom() { + return RelationshipKind { "inheritsFrom" }; + } + /** + A symbol A serves as a default implementation of an interface requirement B. + + The implied inverse of this relationship is an interface requirement B + has a default implementation of A. + */ + static inline RelationshipKind DefaultImplementationOf() { + return RelationshipKind { "defaultImplementationOf" }; + } + /** + A symbol A overrides another symbol B, such as through inheritance. + + The implied inverse of this relationship is a symbol A is the base + of symbol B. + */ + static inline RelationshipKind Overrides() { + return RelationshipKind { "overrides" }; + } + /** + A symbol A is a requirement of interface B. + + The implied inverse of this relationship is an interface B + has a requirement of A. + */ + static inline RelationshipKind RequirementOf() { + return RelationshipKind { "requirementOf" }; + } + /** + A symbol A is an optional requirement of interface B. + + The implied inverse of this relationship is an interface B + has an optional requirement of A. + */ + static inline RelationshipKind OptionalRequirementOf() { + return RelationshipKind { "optionalRequirementOf" }; + } + + bool operator==(const RelationshipKind &Other) const { + return Name == Other.Name; + } + + bool operator<(const RelationshipKind &Other) const { + return Name < Other.Name; + } +}; + +/// A relationship between two symbols: an edge in a directed graph. +struct Edge { + SymbolGraphASTWalker *Walker; + + /// The kind of relationship this edge represents. + RelationshipKind Kind; + + /// The precise identifier of the source symbol node. + const ValueDecl *Source; + + /// The precise identifier of the target symbol node. + const ValueDecl *Target; + + void serialize(llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +namespace llvm { +using Edge = swift::symbolgraphgen::Edge; +template <> struct DenseMapInfo { + static inline Edge getEmptyKey() { + return { + nullptr, + { "Empty" }, + nullptr, + nullptr, + }; + } + static inline Edge getTombstoneKey() { + return { + nullptr, + { "Tombstone" }, + nullptr, + nullptr, + }; + } + static unsigned getHashValue(const Edge E) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(E.Kind.Name); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Source)); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Target)); + return H; + } + static bool isEqual(const Edge LHS, const Edge RHS) { + return LHS.Kind == RHS.Kind && + LHS.Source == RHS.Source && + LHS.Target == RHS.Target; + } +}; +} // end namespace llvm + +#endif // SWIFT_SYMBOLGRAPHGEN_EDGE_H diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h new file mode 100644 index 0000000000000..74ac1c196c5b4 --- /dev/null +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -0,0 +1,20 @@ +//===--- FormatVersion.h - Symbol Graph Format Version 00------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H +#define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H + +#define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0 +#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 1 +#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0 + +#endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H diff --git a/lib/SymbolGraphGen/JSON.cpp b/lib/SymbolGraphGen/JSON.cpp new file mode 100644 index 0000000000000..6bb117daaeeb4 --- /dev/null +++ b/lib/SymbolGraphGen/JSON.cpp @@ -0,0 +1,57 @@ +//===--- JSON.cpp - Symbol Graph JSON Helpers -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#include "JSON.h" + +void swift::symbolgraphgen::serialize(const llvm::VersionTuple &VT, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("major", VT.getMajor()); + if (VT.getMinor()) { + OS.attribute("minor", *VT.getMinor()); + } + if (VT.getSubminor()) { + OS.attribute("patch", *VT.getSubminor()); + } + // Despite the name, + // this is not Semantic Versioning "build metadata" + if (VT.getBuild()) { + OS.attribute("prerelease", *VT.getBuild()); + } + }); +} + +void swift::symbolgraphgen::serialize(const llvm::Triple &T, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("architecture", T.getArchName()); + if (!T.getEnvironmentName().empty()) { + OS.attribute("environment", T.getEnvironmentName()); + } + OS.attribute("vendor", T.getVendorName()); + OS.attributeObject("operatingSystem", [&](){ + OS.attribute("name", T.getOSTypeName(T.getOS())); + + unsigned Major; + unsigned Minor; + unsigned Patch; + T.getOSVersion(Major, Minor, Patch); + llvm::VersionTuple OSVersion(Major, Minor, Patch); + + OS.attributeBegin("minimumVersion"); + serialize(OSVersion, OS); + OS.attributeEnd(); + }); + }); +} diff --git a/lib/SymbolGraphGen/JSON.h b/lib/SymbolGraphGen/JSON.h new file mode 100644 index 0000000000000..0c0018da724cb --- /dev/null +++ b/lib/SymbolGraphGen/JSON.h @@ -0,0 +1,45 @@ +//===--- JSON.h - Symbol Graph JSON Helpers -------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_JSON_H +#define SWIFT_SYMBOLGRAPHGEN_JSON_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/AST/GenericSignature.h" + +namespace swift { +namespace symbolgraphgen { + +struct AttributeRAII { + StringRef Key; + llvm::json::OStream &OS; + AttributeRAII(StringRef Key, llvm::json::OStream &OS) + : Key(Key), OS(OS) { + OS.attributeBegin(Key); + } + + ~AttributeRAII() { + OS.attributeEnd(); + } +}; + +void serialize(const llvm::VersionTuple &VT, llvm::json::OStream &OS); +void serialize(const llvm::Triple &T, llvm::json::OStream &OS); + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_JSON_H diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp new file mode 100644 index 0000000000000..441a636454051 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -0,0 +1,380 @@ +//===--- Symbol.cpp - Symbol Graph Node -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/Basic/SourceManager.h" +#include "JSON.h" +#include "Symbol.h" +#include "SymbolGraph.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("identifier", Identifier); + OS.attribute("displayName", DisplayName); + }); +} + +void Symbol::serializeKind(llvm::json::OStream &OS) const { + AttributeRAII A("kind", OS); + switch (VD->getKind()) { + case swift::DeclKind::Class: + serializeKind("swift.class", "Class", OS); + break; + case swift::DeclKind::Struct: + serializeKind("swift.struct", "Structure", OS); + break; + case swift::DeclKind::Enum: + serializeKind("swift.enum", "Enumeration", OS); + break; + case swift::DeclKind::EnumElement: + serializeKind("swift.enum.case", "Case", OS); + break; + case swift::DeclKind::Protocol: + serializeKind("swift.protocol", "Protocol", OS); + break; + case swift::DeclKind::Constructor: + serializeKind("swift.initializer", "Initializer", OS); + break; + case swift::DeclKind::Func: + serializeKind("swift.function", "Function", OS); + break; + case swift::DeclKind::Var: + serializeKind("swift.variable", "Variable", OS); + break; + case swift::DeclKind::TypeAlias: + serializeKind("swift.typealias", "Type Alias", OS); + break; + case swift::DeclKind::InfixOperator: + serializeKind("swift.infixOperator", "Infix Operator", OS); + break; + case swift::DeclKind::PrefixOperator: + serializeKind("swift.prefixOperator", "Prefix Operator", OS); + break; + case swift::DeclKind::PostfixOperator: + serializeKind("swift.postfixOperator", "Postfix Operator", OS); + break; + default: + llvm_unreachable("Unsupported declaration kind for symbol graph"); + } +} + +void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + AttributeRAII A("identifier", OS); + Walker.getSymbolIdentifier(VD).serialize(OS); +} + +void Symbol::serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("names", [&](){ + auto Identifier = Walker.getSymbolIdentifier(VD); + OS.attribute("title", Identifier.SimpleComponents.back()); + // "navigator": null + Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); + // "prose": null + }); +} + +void Symbol::serializePosition(StringRef Key, + unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const { + OS.attributeObject(Key, [&](){ + OS.attribute("line", Line); + OS.attribute("character", ByteOffset); + }); +} + +void Symbol::serializeRange(size_t InitialIndentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const { + OS.attributeObject("range", [&](){ + auto StartLineAndColumn = SourceMgr.getLineAndColumn(Range.Start); + auto StartLine = StartLineAndColumn.first; + auto StartColumn = StartLineAndColumn.second + InitialIndentation; + serializePosition("start", StartLine, StartColumn, OS); + + auto EndLineAndColumn = SourceMgr.getLineAndColumn(Range.End); + auto EndLine = EndLineAndColumn.first; + auto EndColumn = EndLineAndColumn.second + InitialIndentation; + serializePosition("end", EndLine, EndColumn, OS); + }); +} + +void Symbol::serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("docComment", [&](){ + auto LL = Walker.Ctx.getLineList(VD->getRawComment()); + size_t InitialIndentation = LL.getLines().empty() + ? 0 + : markup::measureIndentation(LL.getLines().front().Text); + OS.attributeArray("lines", [&](){ + for (const auto &Line : LL.getLines()) { + // Line object + OS.object([&](){ + // Trim off any initial indentation from the line's + // text and start of its source range, if it has one. + if (Line.Range.isValid()) { + serializeRange(InitialIndentation, + Line.Range, Walker.M.getASTContext().SourceMgr, OS); + } + auto TrimmedLine = Line.Text.drop_front(std::min(InitialIndentation, + Line.FirstNonspaceOffset)); + OS.attribute("text", TrimmedLine); + }); + } + }); // end lines: [] + }); // end docComment: +} + +void Symbol::serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *FD = dyn_cast_or_null(VD)) { + OS.attributeObject("functionSignature", [&](){ + + // Parameters + if (const auto *ParamList = FD->getParameters()) { + if (ParamList->size()) { + OS.attributeArray("parameters", [&](){ + for (const auto *Param : *ParamList) { + auto ExternalName = Param->getArgumentName().str(); + auto InternalName = Param->getParameterName().str(); + + OS.object([&](){ + if (ExternalName.empty()) { + OS.attribute("name", InternalName); + } else { + OS.attribute("name", ExternalName); + if (ExternalName != InternalName && + !InternalName.empty()) { + OS.attribute("internalName", InternalName); + } + } + Walker.serializeDeclarationFragments("declarationFragments", + Param, OS); + }); // end parameter object + } + }); // end parameters: + } + } + + // Returns + if (const auto ReturnType = FD->getResultInterfaceType()) { + Walker.serializeDeclarationFragments("returns", ReturnType, OS); + } + }); + } +} + +void Symbol::serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("name", Param.getName().str()); + OS.attribute("index", Param.getIndex()); + OS.attribute("depth", Param.getDepth()); + }); +} + +void Symbol::serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const { + OS.object([&](){ + switch (Req.getKind()) { + case swift::RequirementKind::Conformance: + OS.attribute("kind", "conformance"); + break; + case swift::RequirementKind::Superclass: + OS.attribute("kind", "superclass"); + break; + case swift::RequirementKind::SameType: + OS.attribute("kind", "sameType"); + break; + case swift::RequirementKind::Layout: + return; + } + OS.attribute("lhs", Req.getFirstType()->getString()); + OS.attribute("rhs", Req.getSecondType()->getString()); + }); +} + +void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { + if (const auto *GC = VD->getAsGenericContext()) { + if (const auto Generics = GC->getGenericSignature()) { + + OS.attributeObject("swiftGenerics", [&](){ + if (!Generics->getGenericParams().empty()) { + OS.attributeArray("parameters", [&](){ + for (const auto Param : Generics->getGenericParams()) { + if (const auto *D = Param->getDecl()) { + if (D->isImplicit()) { + continue; + } + } + serializeGenericParam(*Param, OS); + } + }); // end parameters: + } + + if (!Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + + }); // end swiftGenerics: + } + } +} + +void Symbol::serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *Extension + = dyn_cast_or_null(VD->getInnermostDeclContext())) { + OS.attributeObject("swiftExtension", [&](){ + OS.attribute("definedInModule", Walker.M.getNameStr()); + auto Generics = Extension->getGenericSignature(); + if (Generics && !Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + }); // end swiftExtension: + } +} + +void Symbol::serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + Walker.serializeDeclarationFragments("declarationFragments", VD, OS); +} + +void Symbol::serializeAccessLevelMixin(llvm::json::OStream &OS) const { + OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess())); +} + +llvm::Optional +Symbol::getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const { + switch (AgnosticKind) { + // SPM- and Swift-specific availability. + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + return { "SwiftPM" }; + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + return { "Swift" }; + // Although these are in the agnostic kinds, they are actually a signal + // that there is either platform-specific or completely platform-agnostic. + // They'll be handled below. + case PlatformAgnosticAvailabilityKind::Deprecated: + case PlatformAgnosticAvailabilityKind::Unavailable: + case PlatformAgnosticAvailabilityKind::None: + break; + } + + // Platform-specific availability. + switch (Kind) { + case swift::PlatformKind::iOS: + return { "iOS" }; + case swift::PlatformKind::OSX: + return { "macOS" }; + case swift::PlatformKind::tvOS: + return { "tvOS" }; + case swift::PlatformKind::watchOS: + return { "watchOS" }; + case swift::PlatformKind::iOSApplicationExtension: + return { "iOSAppExtension" }; + case swift::PlatformKind::OSXApplicationExtension: + return { "macOSAppExtension" }; + case swift::PlatformKind::tvOSApplicationExtension: + return { "tvOSAppExtension" }; + case swift::PlatformKind::watchOSApplicationExtension: + return { "watchOSAppExtension" }; + // Platform-agnostic availability, such as "unconditionally deprecated" + // or "unconditionally obsoleted". + case swift::PlatformKind::none: + return None; + } +} + +void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { + SmallVector Availabilities; + for (const auto *Attr : VD->getAttrs()) { + if (const auto *AvAttr = dyn_cast(Attr)) { + Availabilities.push_back(AvAttr); + } + } + if (Availabilities.empty()) { + return; + } + + OS.attributeArray("availability", [&](){ + for (const auto *AvAttr : Availabilities) { + OS.object([&](){ + auto Domain = getDomain(AvAttr->getPlatformAgnosticAvailability(), + AvAttr->Platform); + if (Domain) { + OS.attribute("domain", *Domain); + } + if (AvAttr->Introduced) { + AttributeRAII Introduced("introduced", OS); + symbolgraphgen::serialize(*AvAttr->Introduced, OS); + } + if (AvAttr->Deprecated) { + AttributeRAII Deprecated("deprecated", OS); + symbolgraphgen::serialize(*AvAttr->Deprecated, OS); + } + if (AvAttr->Obsoleted) { + AttributeRAII Obsoleted("obsoleted", OS); + symbolgraphgen::serialize(*AvAttr->Obsoleted, OS); + } + if (!AvAttr->Message.empty()) { + OS.attribute("message", AvAttr->Message); + } + if (!AvAttr->Rename.empty()) { + OS.attribute("renamed", AvAttr->Rename); + } + if (AvAttr->isUnconditionallyDeprecated()) { + OS.attribute("isUnconditionallyDeprecated", true); + } + if (AvAttr->isUnconditionallyUnavailable()) { + OS.attribute("isUnconditionallyUnavailable", true); + } + }); // end availability object + } + }); // end availability: [] +} + +void Symbol::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + serializeKind(OS); + serializeIdentifier(Walker, OS); + serializeNames(Walker, OS); + serializeDocComment(Walker, OS); + + // "Mixins" + serializeFunctionSignature(Walker, OS); + serializeSwiftGenericMixin(OS); + serializeSwiftExtensionMixin(Walker, OS); + serializeDeclarationFragmentMixin(Walker, OS); + serializeAccessLevelMixin(OS); + serializeAvailabilityMixin(OS); + }); +} diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h new file mode 100644 index 0000000000000..9cdbca5757164 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.h @@ -0,0 +1,128 @@ +//===--- Symbol.h- Symbol Graph Node --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOL_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOL_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Attr.h" +#include "swift/Basic/LLVM.h" +#include "swift/Markup/Markup.h" + +namespace swift { +namespace symbolgraphgen { + +struct AvailabilityDomain; +struct SymbolGraphASTWalker; + +/** + An identifier for a symbol that provides a globally unique identifier suitable for + internal lookups and a locally unique path for human use, such as a URL. + */ +struct SymbolIdentifier { + /** + A string that uniquely identifies a symbol within a module in the event of + ambiguities. A precise identifier need not be human readable. + */ + StringRef PreciseIdentifier; + + /** + The components for a "fully qualified" identifier. + */ + ArrayRef SimpleComponents; + + SymbolIdentifier(llvm::StringRef PreciseIdentifier, + ArrayRef SimpleComponents) + : PreciseIdentifier(PreciseIdentifier), + SimpleComponents(SimpleComponents) { + assert(!PreciseIdentifier.empty()); + } + + void serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("precise", PreciseIdentifier); + OS.attributeArray("simpleComponents", [&](){ + for (auto Component : SimpleComponents) { + OS.value(Component); + } + }); + }); + } + + bool operator==(const SymbolIdentifier &Other) const { + return PreciseIdentifier == Other.PreciseIdentifier && + SimpleComponents == Other.SimpleComponents; + } +}; + +/// A symbol from a module: a node in a graph. +struct Symbol { + const ValueDecl *VD; + + void serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const; + + void serializeKind(llvm::json::OStream &OS) const; + + void serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializePosition(StringRef Key, unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const; + + void serializeRange(size_t InitialIdentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const; + + void serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const; + + void serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const; + + void serializeSwiftGenericMixin(llvm::json::OStream &OS) const; + + void serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeAccessLevelMixin(llvm::json::OStream &OS) const; + + llvm::Optional + getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const; + + void serializeAvailabilityMixin(llvm::json::OStream &OS) const; + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + bool operator==(const Symbol &Other) const { + return VD == Other.VD; + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOL_H diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp new file mode 100644 index 0000000000000..0d41bdcf4a638 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -0,0 +1,68 @@ +//===--- SymbolGraph.cpp - Symbol Graph Data Structure -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "swift/Basic/Version.h" + +#include "FormatVersion.h" +#include "SymbolGraph.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraph::SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion) +: M(M), Target(Target), ModuleVersion(ModuleVersion) {} + +void SymbolGraph::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attributeObject("metadata", [&](){ + { + AttributeRAII FV("formatVersion", OS); + llvm::VersionTuple FormatVersion(SWIFT_SYMBOLGRAPH_FORMAT_MAJOR, + SWIFT_SYMBOLGRAPH_FORMAT_MINOR, + SWIFT_SYMBOLGRAPH_FORMAT_PATCH); + symbolgraphgen::serialize(FormatVersion, OS); + } // end formatVersion: + + auto VersionString = version::getSwiftFullVersion(); + StringRef VersionStringRef(VersionString.c_str(), VersionString.size()); + OS.attribute("generator", VersionStringRef); + }); // end metadata: + + OS.attributeObject("module", [&](){ + OS.attribute("name", M.getNameStr()); + AttributeRAII Platform("platform", OS); + symbolgraphgen::serialize(Target, OS); + }); + + if (ModuleVersion) { + AttributeRAII MV("moduleVersion", OS); + symbolgraphgen::serialize(*ModuleVersion, OS); + } + + OS.attributeArray("symbols", [&](){ + for (const auto *VD: Nodes) { + Symbol S { VD }; + S.serialize(Walker, OS); + } + }); + + OS.attributeArray("relationships", [&](){ + for (const auto Relationship : Edges) { + Relationship.serialize(OS); + } + }); + + }); +} diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h new file mode 100644 index 0000000000000..1e893780e35f3 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -0,0 +1,64 @@ +//===--- SymbolGraph.h - Symbol Graph Data Structure ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/Basic/LLVM.h" +#include "Edge.h" +#include "JSON.h" + +namespace swift { +namespace symbolgraphgen { + +/// A graph of symbols and the relationships between them. +struct SymbolGraph { + /** + The module this symbol graph represents. + */ + ModuleDecl &M; + + /** + The module's target triple. + */ + llvm::Triple Target; + + /** + The semantic version of the module that this symbol graph describes, + if known. + */ + Optional ModuleVersion; + + /** + The symbols in a module: the nodes in the graph. + */ + llvm::SmallPtrSet Nodes; + + /** + The relationships between symbols: the edges in the graph. + */ + llvm::DenseSet Edges; + + SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion = None); + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp new file mode 100644 index 0000000000000..2ce1337d65468 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -0,0 +1,364 @@ +//===--- SymbolGraphASTWalker.cpp - Symbol Graph AST Walker ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "llvm/ADT/StringSwitch.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/USRGeneration.h" +#include "swift/Basic/PrimitiveParsing.h" +#include "swift/Markup/Markup.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M, + const SymbolGraphOptions &Options) + : Options(Options), + M(M), + Graph(M, Options.Target) {} + +/// Returns `true` if the symbol should be included as a node in the graph. +bool SymbolGraphASTWalker::shouldIncludeNode(const Decl *D) const { + // If this decl isn't in this module, don't record it, + // as it will appear elsewhere in its module's symbol graph. + if (D->getModuleContext()->getName() != M.getName()) { + return false; + } + + // Implicit declarations are probably not going to have documentation, + // so don't record it in the symbol graph. + if (D->isImplicit()) { + return false; + } + + // At this point, the declaration must be a ValueDecl. + auto VD = cast(D); + + // Don't record unconditionally private declarations + if (VD->isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) { + return false; + } + + // Don't record effectively internal declarations if specified + if (Options.MinimumAccessLevel > AccessLevel::Internal && + VD->hasUnderscoredNaming()) { + return false; + } + + // Symbols must meet the minimum access level to be included in the graph. + if (VD->getFormalAccess() < Options.MinimumAccessLevel) { + return false; + } + + // Special cases + + auto BaseName = VD->getBaseName().userFacingName(); + + // ${MODULE}Version{Number,String} in ${Module}.h + SmallString<32> VersionNameIdentPrefix { M.getName().str() }; + VersionNameIdentPrefix.append("Version"); + + if (BaseName.startswith(VersionNameIdentPrefix.str())) { + return false; + } + + // Automatically mapped SIMD types + bool ShouldInclude = llvm::StringSwitch(BaseName) +#define MAP_SIMD_TYPE(C_TYPE, _, __) \ + .Case("swift_" #C_TYPE "2", false) \ + .Case("swift_" #C_TYPE "3", false) \ + .Case("swift_" #C_TYPE "4", false) +#include "swift/ClangImporter/SIMDMappedTypes.def" + .Case("SWIFT_TYPEDEFS", false) + .Case("char16_t", false) + .Case("char32_t", false) + .Default(true); + + return ShouldInclude; +} + +bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) { + + switch (D->getKind()) { + // We'll record nodes for the following kinds of declarations. + case swift::DeclKind::Class: + case swift::DeclKind::Struct: + case swift::DeclKind::Enum: + case swift::DeclKind::EnumElement: + case swift::DeclKind::Protocol: + case swift::DeclKind::Constructor: + case swift::DeclKind::Func: + case swift::DeclKind::Var: + case swift::DeclKind::TypeAlias: + break; + + // We'll descend into everything else. + default: + return true; + } + + if (!shouldIncludeNode(D)) { + return false; + } + + auto *VD = cast(D); + Graph.Nodes.insert(VD); + + // Record all of the possible relationships (edges) originating + // with this declaration. + recordMemberRelationship(VD); + recordConformanceRelationships(VD); + recordInheritanceRelationships(VD); + recordDefaultImplementationRelationships(VD); + recordOverrideRelationship(VD); + recordRequirementRelationships(VD); + recordOptionalRequirementRelationships(VD); + + return true; +} + +StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { + auto Found = USRCache.find(VD); + if (Found != USRCache.end()) { + return Found->second; + } + llvm::SmallString<32> Scratch; + llvm::raw_svector_ostream OS(Scratch); + ide::printDeclUSR(VD, OS); + auto USR = Ctx.allocateCopy(Scratch.str()); + USRCache.insert({VD, USR}); + return USR; +} + +SymbolIdentifier +SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { + // Look in the symbol identifier cache for this declartion. + auto Found = SymbolIdentifierCache.find(VD); + if (Found != SymbolIdentifierCache.end()) { + return Found->getSecond(); + } + + // Not found; need to build a symbol identifier and add it to the cache. + auto PreciseIdentifier = getUSR(VD); + llvm::SmallVector SimpleIdentifierChain; + + // Collect the spellings of the fully qualified identifier components. + auto Decl = VD; + while (Decl && !isa(Decl)) { + SmallString<32> Scratch; + Decl->getFullName().getString(Scratch); + SimpleIdentifierChain.push_back(Ctx.allocateCopy(Scratch.str())); + if (const auto *DC = Decl->getDeclContext()) { + if (const auto *Proto = DC->getExtendedProtocolDecl()) { + Decl = Proto; + } else if (const auto *Ext = dyn_cast_or_null(DC->getAsDecl())) { + Decl = Ext->getExtendedNominal(); + } else { + Decl = dyn_cast_or_null(DC->getAsDecl()); + } + } else { + Decl = nullptr; + } + } + + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(SimpleIdentifierChain.begin(), SimpleIdentifierChain.end()); + + SymbolIdentifier Identifier { + PreciseIdentifier, + Ctx.allocateCopy(llvm::makeArrayRef(SimpleIdentifierChain)) + }; + + SymbolIdentifierCache.insert({VD, Identifier}); + return Identifier; +} + +PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { + PrintOptions Opts; + Opts.FunctionDefinitions = false; + Opts.ArgAndParamPrinting = + PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; + Opts.PrintGetSetOnRWProperties = false; + Opts.PrintPropertyAccessors = false; + Opts.PrintSubscriptAccessors = false; + Opts.SkipUnderscoredKeywords = true; + Opts.SkipAttributes = true; + Opts.PrintOverrideKeyword = true; + Opts.PrintImplicitAttrs = false; + Opts.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; + Opts.PrintUserInaccessibleAttrs = false; + Opts.SkipPrivateStdlibDecls = true; + Opts.SkipUnderscoredStdlibProtocols = true; + + Opts.ExclusiveAttrList.clear(); + +#define DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) Opts.ExcludeAttrList.push_back(DAK_##CLASS); +#define TYPE_ATTR(X) Opts.ExcludeAttrList.push_back(TAK_##X); +#include "swift/AST/Attr.def" + + return Opts; +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + VD->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void +SymbolGraphASTWalker::serializeSubheadingDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + auto Options = getDeclarationFragmentsPrintOptions(); + Options.VarInitializers = false; + Options.PrintDefaultArgumentValue = false; + Options.PrintEmptyArgumentNames = false; + Options.PrintOverrideKeyword = false; + VD->print(Printer, Options); +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + T->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void SymbolGraphASTWalker::recordEdge(const ValueDecl *Source, + const ValueDecl *Target, + RelationshipKind Kind) { + if (Target->isPrivateStdlibDecl( + /*treatNonBuiltinProtocolsAsPublic = */false)) { + return; + } + + // There might be relationships on implicit declarations, + // such as overriding implicit @objc init(). + if (Target->isImplicit()) { + return; + } + + Graph.Nodes.insert(Source); + Graph.Nodes.insert(Target); + + Graph.Edges.insert({this, Kind, Source, Target}); +} + +void SymbolGraphASTWalker::recordMemberRelationship(const ValueDecl *VD) { + auto *DC = VD->getDeclContext(); + switch (DC->getContextKind()) { + case DeclContextKind::GenericTypeDecl: + case DeclContextKind::ExtensionDecl: + case swift::DeclContextKind::EnumElementDecl: + return recordEdge(VD, VD->getDeclContext()->getSelfNominalTypeDecl(), + RelationshipKind::MemberOf()); + case swift::DeclContextKind::AbstractClosureExpr: + case swift::DeclContextKind::Initializer: + case swift::DeclContextKind::TopLevelCodeDecl: + case swift::DeclContextKind::SubscriptDecl: + case swift::DeclContextKind::AbstractFunctionDecl: + case swift::DeclContextKind::SerializedLocal: + case swift::DeclContextKind::Module: + case swift::DeclContextKind::FileUnit: + break; + } +} + +void +SymbolGraphASTWalker::recordInheritanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto &InheritanceLoc : NTD->getInherited()) { + auto Ty = InheritanceLoc.getType(); + if (!Ty) { + continue; + } + auto *InheritedTypeDecl = + dyn_cast_or_null(Ty->getAnyNominal()); + if (!InheritedTypeDecl) { + continue; + } + + recordEdge(VD, InheritedTypeDecl, RelationshipKind::InheritsFrom()); + } + } +} + +void SymbolGraphASTWalker::recordDefaultImplementationRelationships( + const ValueDecl *VD) { + if (const auto *Extension = dyn_cast(VD->getDeclContext())) { + if (const auto *Protocol = Extension->getExtendedProtocolDecl()) { + for (const auto *Member : Protocol->getMembers()) { + if (const auto *MemberVD = dyn_cast(Member)) { + if (MemberVD->getFullName().compare(VD->getFullName()) == 0) { + recordEdge(VD, MemberVD, + RelationshipKind::DefaultImplementationOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordRequirementRelationships(const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + recordEdge(VD, Protocol, RelationshipKind::RequirementOf()); + } + } +} + +void SymbolGraphASTWalker::recordOptionalRequirementRelationships( + const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + if (const auto *ClangDecl = VD->getClangDecl()) { + if (const auto *Method = dyn_cast(ClangDecl)) { + if (Method->isOptional()) { + recordEdge(VD, Protocol, + RelationshipKind::OptionalRequirementOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordConformanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto *Conformance : NTD->getAllConformances()) { + recordEdge(VD, Conformance->getProtocol(), + RelationshipKind::ConformsTo()); + } + } +} + +void SymbolGraphASTWalker::recordOverrideRelationship(const ValueDecl *VD) { + if (const auto *Override = VD->getOverriddenDecl()) { + recordEdge(VD, Override, RelationshipKind::Overrides()); + } +} diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h new file mode 100644 index 0000000000000..fb3e3dc9b6d0d --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -0,0 +1,161 @@ +//===--- SymbolGraphASTWalker.h - Symbol Graph AST Walker -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H + +#include "swift/Basic/LLVM.h" +#include "swift/IDE/SourceEntityWalker.h" +#include "swift/Markup/Markup.h" + +#include "SymbolGraph.h" + +namespace swift { + +class Decl; +class Type; +class ValueDecl; + +namespace symbolgraphgen { + +struct SymbolIdentifier; +struct SymbolGraph; +struct SymbolGraphOptions; + +/** + The `SymbolGraphASTWalker` is the core implementation that builds a + symbol graph. It walks a module for declarations, recording facts about + symbols and relationships between them. + */ +struct SymbolGraphASTWalker : public SourceEntityWalker { + /// Options for collecting and serialization. + const SymbolGraphOptions &Options; + + /// The module that this symbol graph will represent. + const ModuleDecl &M; + + /// The symbol graph. + SymbolGraph Graph; + + /// A context for allocations. + markup::MarkupContext Ctx; + + /// A cache of identifiers for declarations that may be seen more than once. + llvm::DenseMap SymbolIdentifierCache; + + /// A cache of USRs for declarations. + llvm::DenseMap USRCache; + + // MARK: - + + SymbolGraphASTWalker(ModuleDecl &M, const SymbolGraphOptions &Options); + virtual ~SymbolGraphASTWalker() {} + + // MARK: - + + /// Returns `true` if the symbol should be included as a node in the graph. + bool shouldIncludeNode(const Decl *D) const; + + virtual bool walkToDeclPre(Decl *D, CharSourceRange Range); + + // MARK: - Utilities and Conversions + + /// Get the USR of a declaration and add it to the local allocator. + StringRef getUSR(const ValueDecl *VD); + + /// Returns a `SymbolIdentifier` for a given declaration. + SymbolIdentifier getSymbolIdentifier(const ValueDecl *VD); + + // MARK: - Declaration Fragments + + /// Get the base print options for declaration fragments. + PrintOptions getDeclarationFragmentsPrintOptions() const; + + /// Serialize the overall declaration fragments for a `ValueDecl`. + void + serializeDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration fragments for a `ValueDecl` when it is viewed + /// as a subheading and/or part of a larger group of symbol listings. + void + serializeSubheadingDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration for a type declaration. + void + serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS); + + // MARK: - Relationships (Edges) + + /** + Record a relationship between two declarations as an edge in the graph. + + \param Source The declaration serving as the source of the edge in the + directed graph. + \param Target The declaration serving as the target of the edge in the + directed graph. + \param Kind The kind of relationship the edge represents. + */ + void recordEdge(const ValueDecl *Source, const ValueDecl *Target, + RelationshipKind Kind); + + /** + Record a MemberOf relationship, if the given declaration is nested + in another. + */ + void recordMemberRelationship(const ValueDecl *VD); + + /** + Record InheritsFrom relationships for every class from which the + declaration inherits. + */ + void recordInheritanceRelationships(const ValueDecl *VD); + + /** + If the declaration is a default implementation in a protocol extension, + record a DefaultImplementationOf relationship between the declaration and + the requirement. + */ + void recordDefaultImplementationRelationships(const ValueDecl *VD); + + /** + Record a RequirementOf relationship if the declaration is a requirement + of a protocol. + */ + void recordRequirementRelationships(const ValueDecl *VD); + + /** + If the declaration is an Objective-C-based optional protocol requirement, + record an OptionalRequirementOf relationship between the declaration + and its containing protocol. + */ + void recordOptionalRequirementRelationships(const ValueDecl *VD); + + /** + Record ConformsTo relationships for each protocol conformance of + the declaration. + */ + void recordConformanceRelationships(const ValueDecl *VD); + + /** + Records an Overrides relationship if the given declaration + overrides another. + */ + void recordOverrideRelationship(const ValueDecl *VD); +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H diff --git a/lib/SymbolGraphGen/SymbolGraphGen.cpp b/lib/SymbolGraphGen/SymbolGraphGen.cpp new file mode 100644 index 0000000000000..667d7b3e328c4 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphGen.cpp @@ -0,0 +1,56 @@ +//===--- SymbolGraphGen.cpp - Symbol Graph Generator Entry Point ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" +#include "swift/AST/Module.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +// MARK: - Main Entry Point + +/// Emit a symbol graph JSON file for a `ModuleDecl`. +int +symbolgraphgen::emitSymbolGraphForModule(ModuleDecl *M, + const SymbolGraphOptions &Options) { + SymbolGraphASTWalker Walker(*M, Options); + SmallVector ModuleDecls; + M->getDisplayDecls(ModuleDecls); + + llvm::errs() << ModuleDecls.size() + << " top-level declarations in this module.\n"; + + for (auto *Decl : ModuleDecls) { + Walker.walk(Decl); + } + + llvm::errs() + << "Found " << Walker.Graph.Nodes.size() << " symbols and " + << Walker.Graph.Edges.size() << " relationships.\n"; + + std::error_code Error; + llvm::raw_fd_ostream OS(Options.OutputPath, Error, llvm::sys::fs::FA_Write); + if (Error) { + llvm::errs() << "Couldn't open output file for writing: " + << Error.message() << "\n"; + return EXIT_FAILURE; + } + + llvm::json::OStream J(OS, Options.PrettyPrint ? 2 : 0); + Walker.Graph.serialize(Walker, J); + + return EXIT_SUCCESS; +} diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 1f3fea838c059..f62466853aab7 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -56,19 +56,78 @@ static bool isGlobalOrStaticVar(VarDecl *VD) { return VD->isStatic() || VD->getDeclContext()->isModuleScopeContext(); } +void TBDGenVisitor::addSymbolInternal(StringRef name, + llvm::MachO::SymbolKind kind, + bool isLinkerDirective) { + if (!isLinkerDirective && Opts.LinkerDirectivesOnly) + return; + Symbols.addSymbol(kind, name, Targets); + if (StringSymbols && kind == SymbolKind::GlobalSymbol) { + auto isNewValue = StringSymbols->insert(name).second; + (void)isNewValue; + assert(isNewValue && "symbol appears twice"); + } +} + +static Optional getDeclMoveOSVersion(Decl *D) { + for (auto *attr: D->getAttrs()) { + if (auto *ODA = dyn_cast(attr)) { + if (ODA->isActivePlatform(D->getASTContext())) + return ODA->MovedVersion; + } + } + return None; +} + +void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name, + llvm::MachO::SymbolKind kind) { + if (kind != llvm::MachO::SymbolKind::GlobalSymbol) + return; + if (!TopLevelDecl) + return; + auto MovedVer = getDeclMoveOSVersion(TopLevelDecl); + if (!MovedVer.hasValue()) + return; + assert(MovedVer.hasValue()); + unsigned Major[2]; + unsigned Minor[2]; + Major[1] = MovedVer->getMajor(); + Minor[1] = MovedVer->getMinor().hasValue() ? *MovedVer->getMinor(): 0; + auto AvailRange = AvailabilityInference::availableRange(TopLevelDecl, + TopLevelDecl->getASTContext()).getOSVersion(); + assert(AvailRange.hasLowerEndpoint() && + "cannot find the start point of availability"); + if (!AvailRange.hasLowerEndpoint()) + return; + assert(AvailRange.getLowerEndpoint() < *MovedVer); + if (AvailRange.getLowerEndpoint() >= *MovedVer) + return; + Major[0] = AvailRange.getLowerEndpoint().getMajor(); + Minor[0] = AvailRange.getLowerEndpoint().getMinor().hasValue() ? + AvailRange.getLowerEndpoint().getMinor().getValue() : 0; + for (auto CurMaj = Major[0]; CurMaj <= Major[1]; ++ CurMaj) { + unsigned MinRange[2] = {0, 31}; + if (CurMaj == Major[0]) + MinRange[0] = Minor[0]; + if (CurMaj == Major[1]) + MinRange[1] = Minor[1]; + for (auto CurMin = MinRange[0]; CurMin != MinRange[1]; ++ CurMin) { + llvm::SmallString<64> Buffer; + llvm::raw_svector_ostream OS(Buffer); + OS << "$ld$hide$os" << CurMaj << "." << CurMin << "$" << name; + addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol, + /*LinkerDirective*/true); + } + } +} + void TBDGenVisitor::addSymbol(StringRef name, SymbolKind kind) { // The linker expects to see mangled symbol names in TBD files, so make sure // to mangle before inserting the symbol. SmallString<32> mangled; llvm::Mangler::getNameWithPrefix(mangled, name, DataLayout); - - Symbols.addSymbol(kind, mangled, Targets); - - if (StringSymbols && kind == SymbolKind::GlobalSymbol) { - auto isNewValue = StringSymbols->insert(mangled).second; - (void)isNewValue; - assert(isNewValue && "symbol appears twice"); - } + addSymbolInternal(mangled, kind); + addLinkerDirectiveSymbols(mangled, kind); } void TBDGenVisitor::addSymbol(SILDeclRef declRef) { @@ -641,6 +700,10 @@ static bool isApplicationExtensionSafe(const LangOptions &LangOpts) { llvm::sys::Process::GetEnv("LD_APPLICATION_EXTENSION_SAFE"); } +static bool hasLinkerDirective(Decl *D) { + return getDeclMoveOSVersion(D).hasValue(); +} + static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, StringSet *symbols, llvm::raw_ostream *os, @@ -688,8 +751,13 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, visitor.addMainIfNecessary(file); - for (auto d : decls) + for (auto d : decls) { + if (opts.LinkerDirectivesOnly && !hasLinkerDirective(d)) + continue; + visitor.TopLevelDecl = d; + SWIFT_DEFER { visitor.TopLevelDecl = nullptr; }; visitor.visit(d); + } }; if (singleFile) { diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index a76da073ef8cf..389528564cd65 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -54,8 +54,12 @@ class TBDGenVisitor : public ASTVisitor { const UniversalLinkageInfo &UniversalLinkInfo; ModuleDecl *SwiftModule; const TBDGenOptions &Opts; + Decl* TopLevelDecl = nullptr; private: + void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind, + bool isLinkerDirective = false); + void addLinkerDirectiveSymbols(StringRef name, llvm::MachO::SymbolKind kind); void addSymbol(StringRef name, llvm::MachO::SymbolKind kind = llvm::MachO::SymbolKind::GlobalSymbol); diff --git a/stdlib/private/CMakeLists.txt b/stdlib/private/CMakeLists.txt index 45cf31363574b..9e900d1178773 100644 --- a/stdlib/private/CMakeLists.txt +++ b/stdlib/private/CMakeLists.txt @@ -18,7 +18,8 @@ if(SWIFT_BUILD_SDK_OVERLAY) add_subdirectory(OSLog) - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list_intersect("${SWIFT_APPLE_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) + if(building_darwin_sdks) add_subdirectory(StdlibUnittestFoundationExtras) if (SWIFT_INCLUDE_TESTS) add_subdirectory(SwiftReflectionTest) diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 710ccede83171..9614bfbdcbc04 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -40,6 +40,7 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/StdlibUnittest/InterceptTraps.cpp b/stdlib/private/StdlibUnittest/InterceptTraps.cpp index 377397bebb9bf..2e76ac6e84aea 100644 --- a/stdlib/private/StdlibUnittest/InterceptTraps.cpp +++ b/stdlib/private/StdlibUnittest/InterceptTraps.cpp @@ -26,6 +26,9 @@ #include "swift/Runtime/Config.h" +// WebAssembly: no signals on WASI yet +#ifndef __wasi__ + static void CrashCatcher(int Sig) { const char *Msg; switch (Sig) { @@ -65,11 +68,16 @@ VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) { } #endif +#endif // __wasi__ + SWIFT_CC(swift) SWIFT_RUNTIME_LIBRARY_VISIBILITY extern "C" void installTrapInterceptor() { // Disable buffering on stdout so that everything is printed before crashing. setbuf(stdout, 0); +// WebAssembly: no signals on WASI +#ifndef __wasi__ + #if defined(_WIN32) _set_abort_behavior(0, _WRITE_ABORT_MSG); #endif @@ -85,5 +93,6 @@ void installTrapInterceptor() { signal(SIGBUS, CrashCatcher); signal(SIGSYS, CrashCatcher); #endif -} +#endif // __wasi__ +} diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index 3bdee4f03d32f..d793930527e71 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -41,7 +41,7 @@ import SwiftPrivateLibcExtras import SwiftPrivateThreadExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -562,7 +562,12 @@ class _InterruptibleSleep { return } +#if os(WASI) +// WebAssembly/WASI on wasm32 is the only 32-bit platform with Int64 time_t + var timeout = timeval(tv_sec: time_t(duration), tv_usec: 0) +#else var timeout = timeval(tv_sec: duration, tv_usec: 0) +#endif var readFDs = _stdlib_fd_set() var writeFDs = _stdlib_fd_set() diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index 20bb2b2c9376f..bc8bd4fe980e0 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -14,7 +14,7 @@ import SwiftPrivate import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index c67f1778d8a3b..51ac487875af9 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -18,7 +18,7 @@ import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Foundation import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -748,6 +748,8 @@ extension ProcessTerminationStatus { case .signal(let signal): #if os(Windows) return CInt(signal) == SIGILL +#elseif os(WASI) + return false #else return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP #endif @@ -1746,6 +1748,7 @@ public enum OSVersion : CustomStringConvertible { case windowsCygnus case windows case haiku + case wasi public var description: String { switch self { @@ -1777,6 +1780,8 @@ public enum OSVersion : CustomStringConvertible { return "Windows" case .haiku: return "Haiku" + case .wasi: + return "WASI" } } } @@ -1821,6 +1826,8 @@ func _getOSVersion() -> OSVersion { return .windows #elseif os(Haiku) return .haiku +#elseif os(WASI) + return .wasi #else let productVersion = _getSystemVersionPlistProperty("ProductVersion")! let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion) diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 2e9c627487a4f..f827f6e2fe956 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -23,7 +23,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2) -#elseif os(Linux) +#elseif os(Linux) || os(WASI) let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0) #elseif os(Android) #if arch(arm) || arch(i386) @@ -43,6 +43,8 @@ public func pointerToSwiftCoreSymbol(name: String) -> UnsafeMutableRawPointer? { #if os(Windows) return unsafeBitCast(GetProcAddress(hStdlibCore, name), to: UnsafeMutableRawPointer?.self) +#elseif os(WASI) + fatalError("\(#function) is not supported on WebAssembly") #else return dlsym(RTLD_DEFAULT, name) #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index 22979ebc7ce8b..a4545a05728ae 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c index 4ef6fdbff8537..26d2402db4626 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // posix_spawn is not available on Android or Windows (MSVC). -#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) +#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__wasi__) #include "swift/Runtime/Config.h" diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index e95e07e142c3c..84ab3b44398b2 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -13,7 +13,7 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -21,6 +21,9 @@ import WinSDK #endif internal func _signalToString(_ signal: Int) -> String { +#if os(WASI) + return "unsupported" +#else switch CInt(signal) { case SIGILL: return "SIGILL" case SIGABRT: return "SIGABRT" @@ -33,6 +36,7 @@ internal func _signalToString(_ signal: Int) -> String { #endif default: return "SIG???? (\(signal))" } +#endif // os(WASI) } public enum ProcessTerminationStatus : CustomStringConvertible { @@ -141,6 +145,15 @@ public func waitProcess(_ process: HANDLE) -> ProcessTerminationStatus { } return .exit(Int(status)) } +#elseif os(WASI) +// Oops, we can't launch tests in subprocesses yet! +public func spawnChild(_ args: [String]) + -> (pid: pid_t, stdinFD: CInt, stdoutFD: CInt, stderrFD: CInt) { + fatalError("Not supported on WebAssembly!") +} +public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus { + fatalError("Not supported on WebAssembly!") +} #else // posix_spawn is not available on Android. // posix_spawn is not available on Haiku. diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index 6fa2c06b6f6ee..68e955f7d648a 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -13,14 +13,14 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { -#if os(Android) || os(Haiku) || os(Windows) +#if os(Android) || os(Haiku) || os(Windows) || os(WASI) preconditionFailure("mkstemps doesn't work on your platform") #else var utf8CStr = template.utf8CString @@ -125,6 +125,8 @@ public func _stdlib_pipe() -> (readEnd: CInt, writeEnd: CInt, error: CInt) { let ret = fds.withUnsafeMutableBufferPointer { unsafeFds -> CInt in #if os(Windows) return _pipe(unsafeFds.baseAddress, 0, 0) +#elseif os(WASI) + fatalError("no pipes on WebAssembly") #else return pipe(unsafeFds.baseAddress) #endif diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index 97e2cc0d2af5b..82a68f1962560 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -14,6 +14,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WASI Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 002aef8e74145..c9d34e35017c2 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -17,7 +17,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -98,6 +98,9 @@ public func _stdlib_thread_create_block( } else { return (0, ThreadHandle(bitPattern: threadID)) } +#elseif os(WASI) + // WASI environment has a only single thread + return (0, nil) #else var threadID = _make_pthread_t() let result = pthread_create(&threadID, nil, @@ -128,6 +131,9 @@ public func _stdlib_thread_join( } else { return (CInt(result), nil) } +#elseif os(WASI) + // WASI environment has a only single thread + return (0, nil) #else var threadResultRawPtr: UnsafeMutableRawPointer? let result = pthread_join(thread, &threadResultRawPtr) diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9f5e183447021..8bef3340ec82a 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -36,6 +36,8 @@ public struct _stdlib_thread_barrier_t { #elseif os(Cygwin) || os(FreeBSD) var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? +#elseif os(WASI) + // No pthread for WASI #else var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? @@ -67,6 +69,8 @@ public func _stdlib_thread_barrier_init( barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) InitializeConditionVariable(barrier.pointee.cond!) +#elseif os(WASI) + // WASI environment has a only single thread #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) if pthread_mutex_init(barrier.pointee.mutex!, nil) != 0 { @@ -89,6 +93,8 @@ public func _stdlib_thread_barrier_destroy( #if os(Windows) // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_destroy(barrier.pointee.cond!) != 0 { // FIXME: leaking memory, leaking a mutex. @@ -99,11 +105,14 @@ public func _stdlib_thread_barrier_destroy( return -1 } #endif + +#if !os(WASI) barrier.pointee.cond!.deinitialize(count: 1) barrier.pointee.cond!.deallocate() barrier.pointee.mutex!.deinitialize(count: 1) barrier.pointee.mutex!.deallocate() +#endif return 0 } @@ -113,6 +122,8 @@ public func _stdlib_thread_barrier_wait( ) -> CInt { #if os(Windows) AcquireSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_mutex_lock(barrier.pointee.mutex!) != 0 { return -1 @@ -127,6 +138,8 @@ public func _stdlib_thread_barrier_wait( return -1 } ReleaseSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_wait(barrier.pointee.cond!, barrier.pointee.mutex!) != 0 { return -1 @@ -144,6 +157,8 @@ public func _stdlib_thread_barrier_wait( #if os(Windows) WakeAllConditionVariable(barrier.pointee.cond!) ReleaseSRWLockExclusive(barrier.pointee.mutex!) +#elseif os(WASI) + // WASI environment has a only single thread #else if pthread_cond_broadcast(barrier.pointee.cond!) != 0 { return -1 diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index fa15c1c9c889c..2f5c8b4f29cbf 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -61,6 +61,9 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(stubs) add_subdirectory(core) add_subdirectory(SwiftOnoneSupport) + if(WASI IN_LIST SWIFT_SDKS) + add_subdirectory(WASI) + endif() endif() # Build differentiable programming support library only if enabled. diff --git a/stdlib/public/Darwin/ARKit/CMakeLists.txt b/stdlib/public/Darwin/ARKit/CMakeLists.txt index df79968511cb8..f8e3eeff16388 100644 --- a/stdlib/public/Darwin/ARKit/CMakeLists.txt +++ b/stdlib/public/Darwin/ARKit/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_target_library(swiftARKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_ TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch GLKit SceneKit simd Foundation AVFoundation SpriteKit CoreMedia QuartzCore ModelIO CoreFoundation CoreAudio ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK ARKit + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_ARKIT_IOS} INSTALL_IN_COMPONENT sdk-overlay diff --git a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt index ad2afdf90eced..e77e6e3618d0b 100644 --- a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt +++ b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt @@ -16,7 +16,7 @@ add_swift_target_library(swiftAVFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYP TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation CoreMedia QuartzCore XPC CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift index 031c714a93f11..931ed4408781f 100644 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ b/stdlib/public/Darwin/Foundation/Data.swift @@ -1402,7 +1402,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl if newValue == 0 { return nil } else if InlineData.canStore(count: newValue) { - return .inline(InlineData()) + return .inline(InlineData(count: newValue)) } else if InlineSlice.canStore(count: newValue) { return .slice(InlineSlice(count: newValue)) } else { diff --git a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt index 2befab06cdfef..0700ae75b0d6b 100644 --- a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt +++ b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt @@ -7,10 +7,11 @@ add_swift_target_library(swiftMediaPlayer ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPE "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch simd Foundation AVFoundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI FRAMEWORK_DEPENDS_WEAK MediaPlayer DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_MEDIAPLAYER_IOS} diff --git a/stdlib/public/Darwin/Photos/CMakeLists.txt b/stdlib/public/Darwin/Photos/CMakeLists.txt index e59dc5b2445af..7d8a8aae1e786 100644 --- a/stdlib/public/Darwin/Photos/CMakeLists.txt +++ b/stdlib/public/Darwin/Photos/CMakeLists.txt @@ -8,7 +8,7 @@ add_swift_target_library(swiftPhotos ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR OSX SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/Vision/CMakeLists.txt b/stdlib/public/Darwin/Vision/CMakeLists.txt index c956ccd3ef2c6..5fd9cb4202f1e 100644 --- a/stdlib/public/Darwin/Vision/CMakeLists.txt +++ b/stdlib/public/Darwin/Vision/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftVision ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation XPC CoreFoundation ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation CoreFoundation ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/WatchKit/CMakeLists.txt b/stdlib/public/Darwin/WatchKit/CMakeLists.txt index dbe216c09d61c..37431bd22ceff 100644 --- a/stdlib/public/Darwin/WatchKit/CMakeLists.txt +++ b/stdlib/public/Darwin/WatchKit/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftWatchKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS WATCHOS WATCHOS_SIMULATOR SWIFT_MODULE_DEPENDS_WATCHOS Darwin HomeKit CoreGraphics UIKit Dispatch SceneKit simd Foundation MapKit CoreLocation CoreFoundation ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK WatchKit diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 90fba95ec1b4e..cc468ff668577 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -39,7 +39,7 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU + TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU WASI INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) @@ -62,7 +62,8 @@ foreach(sdk ${SWIFT_SDKS}) NOT "${sdk}" STREQUAL "FREEBSD" AND NOT "${sdk}" STREQUAL "ANDROID" AND NOT "${sdk}" STREQUAL "CYGWIN" AND - NOT "${sdk}" STREQUAL "HAIKU") + NOT "${sdk}" STREQUAL "HAIKU" AND + NOT "${sdk}" STREQUAL "WASI") continue() endif() diff --git a/stdlib/public/Platform/Glibc.swift.gyb b/stdlib/public/Platform/Glibc.swift.gyb index 2de990adfffaf..dc5ca8b711484 100644 --- a/stdlib/public/Platform/Glibc.swift.gyb +++ b/stdlib/public/Platform/Glibc.swift.gyb @@ -69,3 +69,9 @@ public let ${prefix}_TRUE_MIN = ${type}.leastNonzeroMagnitude #endif % end %end + +#if os(WASI) +// WebAssembly's error.h uses a macro that Swift can't import. +public let EINTR:Int32 = 27 +public let EINVAL:Int32 = 28 +#endif diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index f5e54c7da1283..fce8fb75eadf3 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -366,6 +366,8 @@ public var SIG_IGN: _crt_signal_t { public var SIG_ERR: _crt_signal_t { return unsafeBitCast(-1, to: _crt_signal_t.self) } +#elseif os(WASI) +// WebAssembly/WASI doesn't have signals. #else internal var _ignore = _UnsupportedPlatformError() #endif @@ -380,7 +382,7 @@ public var SEM_FAILED: UnsafeMutablePointer? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // The value is ABI. Value verified to be correct for OS X, iOS, watchOS, tvOS. return UnsafeMutablePointer(bitPattern: -1) -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) // The value is ABI. Value verified to be correct on Glibc. return UnsafeMutablePointer(bitPattern: 0) #else diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 12de1073b6317..b6171c8c321a0 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -126,10 +126,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/math.h" export * } +% if CMAKE_SDK != "WASI": module setjmp { header "${GLIBC_INCLUDE_PATH}/setjmp.h" export * } +% end module signal { header "${GLIBC_INCLUDE_PATH}/signal.h" export * @@ -319,6 +321,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dirent.h" export * } +% if CMAKE_SDK != "WASI": module dl { header "${GLIBC_INCLUDE_PATH}/link.h" export * @@ -327,6 +330,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dlfcn.h" export * } +% end module fcntl { header "${GLIBC_INCLUDE_PATH}/fcntl.h" export * @@ -335,10 +339,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/fnmatch.h" export * } +% if CMAKE_SDK != "WASI": module grp { header "${GLIBC_INCLUDE_PATH}/grp.h" export * } +% end module ioctl { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ioctl.h" export * @@ -347,12 +353,14 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/libgen.h" export * } +% if CMAKE_SDK != "WASI": module net { module if { header "${GLIBC_INCLUDE_PATH}/net/if.h" export * } } +% end module netinet { module in { header "${GLIBC_INCLUDE_PATH}/netinet/in.h" @@ -369,6 +377,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/poll.h" export * } +% if CMAKE_SDK != "WASI": module pthread { header "${GLIBC_INCLUDE_PATH}/pthread.h" export * @@ -377,6 +386,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/pwd.h" export * } +% end module regex { header "${GLIBC_INCLUDE_PATH}/regex.h" export * @@ -422,18 +432,22 @@ module SwiftGlibc [system] { } % end +% if CMAKE_SDK != "WASI": module ipc { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ipc.h" export * } +% end module mman { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/mman.h" export * } +% if CMAKE_SDK != "WASI": module msg { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/msg.h" export * } +% end module resource { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/resource.h" export * @@ -442,7 +456,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/select.h" export * } -% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU": +% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU" and CMAKE_SDK != "WASI": module sendfile { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/sendfile.h" export * @@ -492,10 +506,12 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/utsname.h" export * } +% if CMAKE_SDK != "WASI": module wait { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/wait.h" export * } +% end } % if CMAKE_SDK in ["LINUX", "FREEBSD"]: module sysexits { @@ -503,23 +519,29 @@ module SwiftGlibc [system] { export * } % end +% if CMAKE_SDK != "WASI": module termios { header "${GLIBC_INCLUDE_PATH}/termios.h" export * } +% end module unistd { header "${GLIBC_INCLUDE_PATH}/unistd.h" export * } +% if CMAKE_SDK != "WASI": module utime { header "${GLIBC_INCLUDE_PATH}/utime.h" export * } +% end } } +% if CMAKE_SDK != "WASI": module CUUID [system] { header "${GLIBC_INCLUDE_PATH}/uuid/uuid.h" link "uuid" export * } +% end diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index aae8ea9452de0..c93afa1d25067 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -1007,10 +1007,22 @@ class EnumTypeInfoBuilder { // NoPayloadEnumImplStrategy if (PayloadCases.empty()) { Kind = RecordKind::NoPayloadEnum; - Size += getEnumTagCounts(/*size=*/0, - NoPayloadCases, - /*payloadCases=*/0).numTagBytes; - + switch (NoPayloadCases) { + case 0: + case 1: // Zero or one tag has size = 0, extra_inhab = 0 + NumExtraInhabitants = 0; + break; + default: { // 2 or more tags + auto tagCounts = getEnumTagCounts(/*size=*/0, + NoPayloadCases, + /*payloadCases=*/0); + Size += tagCounts.numTagBytes; + NumExtraInhabitants = + (1 << (tagCounts.numTagBytes * 8)) - tagCounts.numTags; + NumExtraInhabitants = std::min(NumExtraInhabitants, + unsigned(ValueWitnessFlags::MaxNumExtraInhabitants)); + } + } // SinglePayloadEnumImplStrategy } else if (PayloadCases.size() == 1) { auto *CaseTR = getCaseTypeRef(PayloadCases[0]); diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h index 5725f294885f9..1c60a7561c5b8 100644 --- a/stdlib/public/SwiftShims/LibcShims.h +++ b/stdlib/public/SwiftShims/LibcShims.h @@ -43,6 +43,8 @@ typedef __swift_uint32_t __swift_mode_t; typedef __swift_uint16_t __swift_mode_t; #elif defined(_WIN32) typedef __swift_int32_t __swift_mode_t; +#elif defined(__wasi__) +typedef __swift_uint32_t __swift_mode_t; #else // just guessing typedef __swift_uint16_t __swift_mode_t; #endif @@ -105,7 +107,7 @@ static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { return malloc_size(ptr); } #elif defined(__linux__) || defined(__CYGWIN__) || defined(__ANDROID__) \ - || defined(__HAIKU__) || defined(__FreeBSD__) + || defined(__HAIKU__) || defined(__FreeBSD__) || defined(__wasi__) static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { #if defined(__ANDROID__) #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 17 diff --git a/stdlib/public/SwiftShims/RefCount.h b/stdlib/public/SwiftShims/RefCount.h index d9ba9e5c77c62..6e0886a432659 100644 --- a/stdlib/public/SwiftShims/RefCount.h +++ b/stdlib/public/SwiftShims/RefCount.h @@ -1475,25 +1475,13 @@ HeapObject* RefCounts::getHeapObject() { // for use by SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS typedef swift::InlineRefCounts InlineRefCounts; -// __cplusplus -#endif - // These assertions apply to both the C and the C++ declarations. -#if defined(_MSC_VER) && !defined(__clang__) static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), "InlineRefCounts and InlineRefCountsPlaceholder must match"); static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), "InlineRefCounts must be pointer-sized"); static_assert(__alignof(InlineRefCounts) == __alignof(__swift_uintptr_t), "InlineRefCounts must be pointer-aligned"); -#else -_Static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), - "InlineRefCounts and InlineRefCountsPlaceholder must match"); -_Static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), - "InlineRefCounts must be pointer-sized"); -_Static_assert(_Alignof(InlineRefCounts) == _Alignof(__swift_uintptr_t), - "InlineRefCounts must be pointer-aligned"); -#endif #if defined(_WIN32) && defined(_M_ARM64) #if defined(__cplusplus) @@ -1508,4 +1496,6 @@ inline void _Atomic_storage::_Unlock() const n #endif #endif +#endif // !defined(__swift__) + #endif diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 8577fad1653b9..8245a3b9be7e7 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -76,7 +76,7 @@ // SWIFT_RUNTIME_EXPORT on the library it's exported from. /// Attribute used to export symbols from the runtime. -#if defined(__MACH__) +#if defined(__MACH__) || defined(__wasm__) # define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) diff --git a/stdlib/public/WASI/CMakeLists.txt b/stdlib/public/WASI/CMakeLists.txt new file mode 100644 index 0000000000000..451f1f3af57d7 --- /dev/null +++ b/stdlib/public/WASI/CMakeLists.txt @@ -0,0 +1,3 @@ +add_swift_target_library(swiftWasiPthread STATIC IS_STDLIB + Pthread.cpp + INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/WASI/Pthread.cpp b/stdlib/public/WASI/Pthread.cpp new file mode 100644 index 0000000000000..5a64d9bdb4c39 --- /dev/null +++ b/stdlib/public/WASI/Pthread.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: 0BSD +// prototypes taken from opengroup +#include +#include +#include +#include + +#define STUB() do {fprintf(stderr, "FakePthread: unsupported %s\n", __func__);abort();}while(0) + +// mutexes: just no-ops + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + return 0; +} + +// pthread_cond: STUB + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) { + return 0; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { + STUB(); +} + +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, const struct timespec *abstime) { + STUB(); +} + +int pthread_cond_broadcast(pthread_cond_t *cond) { + return 0; +} + +int pthread_cond_signal(pthread_cond_t *cond) { + return 0; +} + +// tls + +int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { + STUB(); +} + +void *pthread_getspecific(pthread_key_t key) { + STUB(); +} + +int pthread_setspecific(pthread_key_t key, const void *value) { + STUB(); +} + +// threads + +pthread_t pthread_self() { + return (pthread_t)1234; +} + +#undef pthread_equal + +int pthread_equal(pthread_t t1, pthread_t t2) { + return t1 == t2; +} + +int pthread_join(pthread_t thread, void **value_ptr) { + STUB(); +} + +int pthread_detach(pthread_t thread) { + STUB(); +} + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { + return 0; +} + +// once + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { + STUB(); +} + +// rwlock + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { + return 0; +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { + return 0; +} + +// named semaphores + +sem_t *sem_open(const char *name, int oflag, ...) { + STUB(); +} diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 93db94f22d136..e143420e9120e 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -346,7 +346,8 @@ extension Array { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -1023,19 +1024,65 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } + + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`, but at least + /// kept at `minimumCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`, but at least kept at `minimumCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) + + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -1054,7 +1101,9 @@ extension Array: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -1083,7 +1132,9 @@ extension Array: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -1124,6 +1175,8 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -1160,7 +1213,7 @@ extension Array: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -1176,30 +1229,40 @@ extension Array: RangeReplaceableCollection { if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty Array") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount @@ -1223,11 +1286,13 @@ extension Array: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() - let newCount = _getCount() - 1 + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") + let newCount = currentCount - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() pointer.moveInitialize(from: pointer + 1, count: newCount - index) @@ -1524,9 +1589,8 @@ extension Array { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1638,19 +1702,12 @@ extension Array { _precondition(subrange.upperBound <= _buffer.endIndex, "Array replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } diff --git a/stdlib/public/core/ArrayShared.swift b/stdlib/public/core/ArrayShared.swift index 1512260c55f3f..3e7d5939bac03 100644 --- a/stdlib/public/core/ArrayShared.swift +++ b/stdlib/public/core/ArrayShared.swift @@ -138,6 +138,23 @@ internal func _growArrayCapacity(_ capacity: Int) -> Int { return capacity * 2 } +@_alwaysEmitIntoClient +internal func _growArrayCapacity( + oldCapacity: Int, minimumCapacity: Int, growForAppend: Bool +) -> Int { + if growForAppend { + if oldCapacity < minimumCapacity { + // When appending to an array, grow exponentially. + return Swift.max(minimumCapacity, _growArrayCapacity(oldCapacity)) + } + return oldCapacity + } + // If not for append, just use the specified capacity, ignoring oldCapacity. + // This means that we "shrink" the buffer in case minimumCapacity is less + // than oldCapacity. + return minimumCapacity +} + //===--- generic helpers --------------------------------------------------===// extension _ArrayBufferProtocol { diff --git a/stdlib/public/core/AtomicInt.swift.gyb b/stdlib/public/core/AtomicInt.swift.gyb index 62217f282ccc5..80514f42c147c 100644 --- a/stdlib/public/core/AtomicInt.swift.gyb +++ b/stdlib/public/core/AtomicInt.swift.gyb @@ -65,7 +65,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( object target: UnsafeMutablePointer, expected: UnsafeMutablePointer, desired: Int) -> Bool { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Int32( target._rawValue, expected.pointee._value, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -82,7 +82,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( public // Existing uses outside stdlib func _swift_stdlib_atomicLoadInt( object target: UnsafeMutablePointer) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = Builtin.atomicload_seqcst_Int32(target._rawValue) return Int(value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -95,7 +95,7 @@ func _swift_stdlib_atomicLoadInt( internal func _swift_stdlib_atomicStoreInt( object target: UnsafeMutablePointer, desired: Int) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) Builtin.atomicstore_seqcst_Int32(target._rawValue, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) Builtin.atomicstore_seqcst_Int64(target._rawValue, desired._value) @@ -111,7 +111,7 @@ func _swift_stdlib_atomicFetch${operation}Int( object target: UnsafeMutablePointer, operand: Int) -> Int { let rawTarget = UnsafeMutableRawPointer(target) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = _swift_stdlib_atomicFetch${operation}Int32( object: rawTarget.assumingMemoryBound(to: Int32.self), operand: Int32(operand)) diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index 4b100f28f7cfe..fcc971f8b6f76 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -61,7 +61,7 @@ internal struct _BridgeStorage { rawValue = Builtin.reinterpretCast(native) } -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) @inlinable @inline(__always) internal init(taggedPayload: UInt) { diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index 07c9ce14af6a7..b29bcc0d6aa59 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -379,7 +379,7 @@ internal var _objectPointerLowSpareBitShift: UInt { } #if arch(i386) || arch(arm) || arch(powerpc64) || arch(powerpc64le) || arch( - s390x) + s390x) || arch(wasm32) @inlinable internal var _objectPointerIsObjCBit: UInt { @inline(__always) get { return 0x0000_0002 } diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index ae1dc04163be5..13275086d2f26 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -67,7 +67,8 @@ extension ContiguousArray { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -655,19 +656,64 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } + + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) + + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -687,7 +733,9 @@ extension ContiguousArray: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -716,7 +764,9 @@ extension ContiguousArray: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -757,6 +807,8 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -793,7 +845,7 @@ extension ContiguousArray: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -809,30 +861,40 @@ extension ContiguousArray: RangeReplaceableCollection { if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty ContiguousArray") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount @@ -856,10 +918,12 @@ extension ContiguousArray: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") let newCount = _getCount() - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() @@ -1106,9 +1170,8 @@ extension ContiguousArray { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1221,19 +1284,12 @@ extension ContiguousArray { _precondition(subrange.upperBound <= _buffer.endIndex, "ContiguousArray replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index 988ef9f7d0679..5ffaacc45ef32 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -46,7 +46,7 @@ extension Dictionary { @inlinable @inline(__always) init(dummy: Void) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeDictionary()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/FloatingPointParsing.swift.gyb b/stdlib/public/core/FloatingPointParsing.swift.gyb index 1225f297d12d5..b4cddc1a467bd 100644 --- a/stdlib/public/core/FloatingPointParsing.swift.gyb +++ b/stdlib/public/core/FloatingPointParsing.swift.gyb @@ -126,25 +126,38 @@ extension ${Self}: LosslessStringConvertible { /// is `nil`. @inlinable // FIXME(sil-serialize-all) public init?(_ text: S) { - let u8 = text.utf8 - - let (result, n): (${Self}, Int) = text.withCString { chars in + let result: ${Self}? = text.withCString { chars in + // TODO: We should change the ABI for + // _swift_stdlib_strtoX_clocale so that it returns + // a boolean `false` for leading whitespace, empty + // string, or invalid character. Then we could avoid + // inlining these checks into every single client. + switch chars[0] { + case 9, 10, 11, 12, 13, 32: + // Reject any input with leading whitespace. + return nil + case 0: + // Reject the empty string + return nil + default: + break + } var result: ${Self} = 0 let endPtr = withUnsafeMutablePointer(to: &result) { _swift_stdlib_strto${cFuncSuffix2[bits]}_clocale(chars, $0) } - return (result, endPtr == nil ? 0 : endPtr! - chars) + // Verify that all the characters were consumed. + if endPtr == nil || endPtr![0] != 0 { + return nil + } + return result } - if n == 0 || n != u8.count - || u8.contains(where: { codeUnit in - // Check if the code unit is either non-ASCII or if isspace(codeUnit) - // would return nonzero when the current locale is the C locale. - codeUnit > 127 || "\t\n\u{b}\u{c}\r ".utf8.contains(codeUnit) - }) { + if let result = result { + self = result + } else { return nil } - self = result } } diff --git a/stdlib/public/core/Hasher.swift b/stdlib/public/core/Hasher.swift index f7098db934aa3..202c2830610ab 100644 --- a/stdlib/public/core/Hasher.swift +++ b/stdlib/public/core/Hasher.swift @@ -160,7 +160,7 @@ extension Hasher { @inline(__always) internal mutating func combine(_ value: UInt) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) combine(UInt32(truncatingIfNeeded: value)) #else combine(UInt64(truncatingIfNeeded: value)) @@ -423,7 +423,7 @@ public struct Hasher { @usableFromInline internal static func _hash(seed: Int, _ value: UInt) -> Int { var state = _State(seed: seed) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(UInt.bitWidth < UInt64.bitWidth) let tbc = _TailBuffer( tail: UInt64(truncatingIfNeeded: value), diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index a00bab337d49f..358696961007b 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -384,7 +384,9 @@ internal func _print_unlocked( // string. Check for Optional first, before checking protocol // conformance below, because an Optional value is convertible to a // protocol if its wrapped type conforms to that protocol. - if _isOptional(type(of: value)) { + // Note: _isOptional doesn't work here when T == Any, hence we + // use a more elaborate formulation: + if _openExistential(type(of: value as Any), do: _isOptional) { let debugPrintable = value as! CustomDebugStringConvertible debugPrintable.debugDescription.write(to: &target) return diff --git a/stdlib/public/core/SetBridging.swift b/stdlib/public/core/SetBridging.swift index 75332860ec8bd..85ce73edb5213 100644 --- a/stdlib/public/core/SetBridging.swift +++ b/stdlib/public/core/SetBridging.swift @@ -20,7 +20,7 @@ internal func _stdlib_NSSet_allObjects(_ object: AnyObject) -> _BridgingBuffer { let nss = unsafeBitCast(object, to: _NSSet.self) let count = nss.count let storage = _BridgingBuffer(count) - nss.getObjects(storage.baseAddress, count: count) + nss.getObjects(storage.baseAddress) return storage } diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 0092fc74ca45e..ae3149002c7c4 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -36,7 +36,7 @@ extension Set { @inlinable @inline(__always) init(dummy: ()) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeSet()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/ShadowProtocols.swift b/stdlib/public/core/ShadowProtocols.swift index bdffbd02ff4e6..0e9bb7128604c 100644 --- a/stdlib/public/core/ShadowProtocols.swift +++ b/stdlib/public/core/ShadowProtocols.swift @@ -175,6 +175,10 @@ internal protocol _NSSet: _NSSetCore { _ buffer: UnsafeMutablePointer, count: Int ) + + @objc(getObjects:) func getObjects( + _ buffer: UnsafeMutablePointer + ) } /// A shadow for the API of NSNumber we will use in the core diff --git a/stdlib/public/core/Slice.swift b/stdlib/public/core/Slice.swift index 3ad2c2be2ebf3..751e88cd10bbe 100644 --- a/stdlib/public/core/Slice.swift +++ b/stdlib/public/core/Slice.swift @@ -215,6 +215,18 @@ extension Slice: Collection { public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { _base._failEarlyRangeCheck(range, bounds: bounds) } + + @_alwaysEmitIntoClient @inlinable + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + try _base.withContiguousStorageIfAvailable { buffer in + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + let slice = UnsafeBufferPointer(rebasing: buffer[start ..< start + count]) + return try body(slice) + } + } } extension Slice: BidirectionalCollection where Base: BidirectionalCollection { @@ -258,6 +270,34 @@ extension Slice: MutableCollection where Base: MutableCollection { _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) } } + + @_alwaysEmitIntoClient @inlinable + public mutating func withContiguousMutableStorageIfAvailable( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + // We're calling `withContiguousMutableStorageIfAvailable` twice here so + // that we don't calculate index distances unless we know we'll use them. + // The expectation here is that the base collection will make itself + // contiguous on the first try and the second call will be relatively cheap. + guard _base.withContiguousMutableStorageIfAvailable({ _ in }) != nil + else { + return nil + } + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + return try _base.withContiguousMutableStorageIfAvailable { buffer in + var slice = UnsafeMutableBufferPointer( + rebasing: buffer[start ..< start + count]) + let copy = slice + defer { + _precondition( + slice.baseAddress == copy.baseAddress && + slice.count == copy.count, + "Slice.withUnsafeMutableBufferPointer: replacing the buffer is not allowed") + } + return try body(&slice) + } + } } diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 1f53f8e7ff4fb..9792ed1cf485f 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -76,7 +76,7 @@ internal struct _SmallString { extension _SmallString { @inlinable @inline(__always) internal static var capacity: Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) return 10 #else return 15 diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 1896d974204b6..2b892c2d52867 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -285,7 +285,7 @@ internal enum _KnownCocoaString { case storage case shared case cocoa -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) case tagged #endif diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 2b7765cd4093e..cc6ed08226e67 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -178,7 +178,7 @@ extension _StringGuts { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout.size == 12, """ the runtime is depending on this, update Reflection.mm and \ this if you change it diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift index 501d81b66ff53..72cf15c5200c7 100644 --- a/stdlib/public/core/StringObject.swift +++ b/stdlib/public/core/StringObject.swift @@ -77,7 +77,7 @@ internal struct _StringObject { internal init(zero: ()) { self._storage = 0 } } -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) @usableFromInline @frozen internal enum Variant { case immortal(UInt) @@ -169,7 +169,7 @@ extension _StringObject { @usableFromInline internal typealias RawBitPattern = (UInt64, UInt64) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // On 32-bit platforms, raw bit conversion is one-way only and uses the same // layout as on 64-bit platforms. @usableFromInline @@ -245,7 +245,7 @@ extension _StringObject { @inlinable @_transparent internal var discriminatedObjectRawBits: UInt64 { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let low32: UInt switch _variant { case .immortal(let bitPattern): @@ -387,7 +387,7 @@ extension _StringObject.Nibbles { extension _StringObject { @inlinable @inline(__always) internal static var nativeBias: UInt { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) return 20 #else return 32 @@ -512,7 +512,7 @@ extension _StringObject { // spare bits (the most significant nibble) in a pointer. let word1 = small.rawBits.0.littleEndian let word2 = small.rawBits.1.littleEndian -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // On 32-bit, we need to unpack the small string. let smallStringDiscriminatorAndCount: UInt64 = 0xFF00_0000_0000_0000 @@ -556,7 +556,7 @@ extension _StringObject { @inlinable @inline(__always) internal init(empty:()) { // Canonical empty pattern: small zero-length string -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( count: 0, variant: .immortal(0), @@ -819,7 +819,7 @@ extension _StringObject { @inline(__always) internal var nativeStorage: __StringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .native(let storage) = _variant else { _internalInvariantFailure() } @@ -832,7 +832,7 @@ extension _StringObject { @inline(__always) internal var sharedStorage: __SharedStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .native(let storage) = _variant else { _internalInvariantFailure() } @@ -846,7 +846,7 @@ extension _StringObject { @inline(__always) internal var cocoaObject: AnyObject { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .bridged(let object) = _variant else { _internalInvariantFailure() } @@ -935,7 +935,7 @@ extension _StringObject { internal init(immortal bufPtr: UnsafeBufferPointer, isASCII: Bool) { let countAndFlags = CountAndFlags( immortalCount: bufPtr.count, isASCII: isASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .immortal(start: bufPtr.baseAddress._unsafelyUnwrappedUnchecked), discriminator: Nibbles.largeImmortal(), @@ -955,7 +955,7 @@ extension _StringObject { @inline(__always) internal init(_ storage: __StringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -969,7 +969,7 @@ extension _StringObject { } internal init(_ storage: __SharedStringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -987,7 +987,7 @@ extension _StringObject { ) { let countAndFlags = CountAndFlags(sharedCount: length, isASCII: isASCII) let discriminator = Nibbles.largeCocoa(providesFastUTF8: providesFastUTF8) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .bridged(cocoa), discriminator: discriminator, @@ -1009,7 +1009,7 @@ extension _StringObject { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout<_StringObject>.size == 12) _internalInvariant(MemoryLayout<_StringObject>.stride == 12) _internalInvariant(MemoryLayout<_StringObject>.alignment == 4) @@ -1079,7 +1079,7 @@ extension _StringObject { } } - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) switch _variant { case .immortal: _internalInvariant(isImmortal) @@ -1099,7 +1099,7 @@ extension _StringObject { let raw = self.rawBits let word0 = ("0000000000000000" + String(raw.0, radix: 16)).suffix(16) let word1 = ("0000000000000000" + String(raw.1, radix: 16)).suffix(16) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) print(""" StringObject(\ <\(word0) \(word1)> \ diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index 8e8a3d29d2133..3e47a43cc3f30 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -46,7 +46,7 @@ private typealias CountAndFlags = _StringObject.CountAndFlags // renamed. The old name must not be used in the new runtime. final internal class __StringStorage : __SwiftNativeNSString, _AbstractStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // The total allocated storage capacity. Note that this includes the required // nul-terminator. internal var _realCapacity: Int @@ -106,7 +106,7 @@ final internal class __StringStorage // for Strings ~1KB or larger, though at this point we're well into our growth // curve. private func determineCodeUnitCapacity(_ desiredCapacity: Int) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // FIXME: Adapt to actual 32-bit allocator. For now, let's arrange things so // that the instance size will be a multiple of 4. let bias = Int(bitPattern: _StringObject.nativeBias) @@ -139,7 +139,7 @@ extension __StringStorage { __StringStorage.self, realCodeUnitCapacity._builtinWordValue, UInt8.self, 1._builtinWordValue, Optional<_StringBreadcrumbs>.self) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) storage._realCapacity = realCodeUnitCapacity storage._count = countAndFlags.count storage._flags = countAndFlags.flags @@ -186,7 +186,7 @@ extension __StringStorage { let count = try initializer(buffer) let countAndFlags = CountAndFlags(mortalCount: count, isASCII: false) - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) storage._count = countAndFlags.count storage._flags = countAndFlags.flags #else @@ -319,7 +319,7 @@ extension __StringStorage { internal func _updateCountAndFlags(newCount: Int, newIsASCII: Bool) { let countAndFlags = CountAndFlags( mortalCount: newCount, isASCII: newIsASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else @@ -463,7 +463,7 @@ final internal class __SharedStringStorage internal var _owner: AnyObject? internal var start: UnsafePointer -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) internal var _count: Int internal var _flags: UInt16 @@ -485,7 +485,7 @@ final internal class __SharedStringStorage ) { self._owner = nil self.start = ptr -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 001d97d517405..1c72e7601ed4c 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -56,6 +56,7 @@ set(swift_runtime_sources MetadataLookup.cpp MutexPThread.cpp MutexWin32.cpp + MutexWASI.cpp Numeric.cpp Once.cpp Portability.cpp @@ -80,6 +81,7 @@ endif(LLVM_ENABLE_ASSERTIONS) set(LLVM_OPTIONAL_SOURCES SwiftRT-COFF.cpp SwiftRT-ELF.cpp + SwiftRT-WASM.cpp ${swift_runtime_sources} ${swift_runtime_objc_sources} ${swift_runtime_leaks_sources}) @@ -88,79 +90,121 @@ set(swift_runtime_library_compile_flags ${swift_runtime_compile_flags}) list(APPEND swift_runtime_library_compile_flags -DswiftCore_EXPORTS) list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/include) -set(sdk "${SWIFT_HOST_VARIANT_SDK}") -if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") - list(REMOVE_ITEM swift_runtime_sources ImageInspectionELF.cpp) - set(static_binary_lnk_file_list) +set(static_binary_lnk_file_list) +set(static_binary_dependencies_list) +macro(add_image_inspection_shared sdk primary_arch inspection_file linkfile_src) + if(${inspection_file} IN_LIST swift_runtime_sources) + list(REMOVE_ITEM swift_runtime_sources ${inspection_file}) + endif() string(TOLOWER "${sdk}" lowercase_sdk) # These two libraries are only used with the static swiftcore add_swift_target_library(swiftImageInspectionShared STATIC - ImageInspectionELF.cpp + ${inspection_file} C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} LINK_FLAGS ${swift_runtime_linker_flags} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + TARGET_SDKS ${sdk} INSTALL_IN_COMPONENT stdlib) foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) set(FragileSupportLibrary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) - add_custom_command_target(swift_image_inspection_${arch}_static + + add_custom_command_target(swift_image_inspection_${lowercase_sdk}_${arch}_static COMMAND "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} OUTPUT "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" DEPENDS ${FragileSupportLibrary}) + + list(APPEND static_binary_dependencies_list ${swift_image_inspection_${lowercase_sdk}_${arch}_static}) add_dependencies(stdlib ${FragileSupportLibrary}) swift_install_in_component(FILES $ DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" COMPONENT stdlib) endforeach() - set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) - set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) - add_custom_command_target(swift_image_inspection_static_primary_arch - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} - OUTPUT - "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibraryPrimary}) + if(NOT "${primary_arch}" STREQUAL "") + set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${primary_arch}) + set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) + add_custom_command_target(swift_image_inspection_static_${lowercase_sdk}_primary_arch + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} + OUTPUT + "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibraryPrimary}) + list(APPEND static_binary_dependencies_list ${swift_image_inspection_static_${lowercase_sdk}_primary_arch}) add_dependencies(stdlib ${FragileSupportLibraryPrimary}) swift_install_in_component(FILES $ DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) + endif() - # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") add_custom_command_target(swift_static_binary_${sdk}_args COMMAND "${CMAKE_COMMAND}" -E copy - "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk" + "${linkfile_src}" "${SWIFTSTATICLIB_DIR}/${linkfile}" OUTPUT "${SWIFTSTATICLIB_DIR}/${linkfile}" DEPENDS - "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk") + "${linkfile_src}") list(APPEND static_binary_lnk_file_list ${swift_static_binary_${sdk}_args}) swift_install_in_component(FILES "${SWIFTSTATICLIB_DIR}/${linkfile}" DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) - add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) - foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) - add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) - endforeach() - add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) - add_dependencies(stdlib static_binary_magic) add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY - ImageInspectionELF.cpp + ${inspection_file} C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} LINK_FLAGS ${swift_runtime_linker_flags} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + TARGET_SDKS ${sdk} INSTALL_IN_COMPONENT never_install) +endmacro() + +set(is_image_inspection_required) +foreach(sdk IN LISTS SWIFT_SDKS) + set(image_inspection_shared_sdk) + set(primary_arch) + set(image_inspection_shared_file) + set(linkfile_src) + + if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") + list(APPEND ELFISH_SDKS ${sdk}) + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/static-executable-args.lnk") + elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") + set(linkfile_src "${SWIFT_SOURCE_DIR}/utils/webassembly/static-executable-args.lnk") + endif() + + if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") + set(image_inspection_shared_sdk "${sdk}") + set(image_inspection_shared_file ImageInspectionELF.cpp) + elseif(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "WASI") + set(image_inspection_shared_sdk "${sdk}") + set(image_inspection_shared_file ImageInspectionELF.cpp) + # Set default arch + set(primary_arch "wasm32") + endif() + + if("${sdk}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") + set(primary_arch ${SWIFT_PRIMARY_VARIANT_ARCH}) + endif() + + if(NOT "${image_inspection_shared_sdk}" STREQUAL "" AND NOT "${primary_arch}" STREQUAL "") + set(is_image_inspection_required TRUE) + add_image_inspection_shared(${image_inspection_shared_sdk} ${primary_arch} ${image_inspection_shared_file} ${linkfile_src}) + endif() +endforeach() + +if(is_image_inspection_required) + add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list} ${static_binary_dependencies_list}) + add_dependencies(stdlib static_binary_magic) endif() add_swift_target_library(swiftRuntime OBJECT_LIBRARY @@ -174,11 +218,14 @@ add_swift_target_library(swiftRuntime OBJECT_LIBRARY set(ELFISH_SDKS) set(COFF_SDKS) +set(WASM_SDKS) foreach(sdk ${SWIFT_CONFIGURED_SDKS}) if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF") list(APPEND ELFISH_SDKS ${sdk}) elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF") list(APPEND COFF_SDKS ${sdk}) + elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") + list(APPEND WASM_SDKS ${sdk}) endif() endforeach() @@ -190,6 +237,16 @@ add_swift_target_library(swiftImageRegistrationObjectELF TARGET_SDKS ${ELFISH_SDKS} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT none) + +add_swift_target_library(swiftImageRegistrationObjectWASM + OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE + SwiftRT-WASM.cpp + C_COMPILE_FLAGS ${SWIFT_RUNTIME_CORE_CXX_FLAGS} + LINK_FLAGS ${SWIFT_RUNTIME_CORE_LINK_FLAGS} + TARGET_SDKS ${WASM_SDKS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + INSTALL_IN_COMPONENT none) + # FIXME(compnerd) this should be compiled twice, once for static and once for # shared. The static version should be used for building the standard library. add_swift_target_library(swiftImageRegistrationObjectCOFF @@ -207,7 +264,8 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS}) set(arch_suffix "${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}") if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF") + "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "COFF" OR + "${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM") # TODO(compnerd) switch to the generator expression when cmake is upgraded # to a version which supports it. # set(swiftrtObject "$") diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 2955a276e63fe..fcdd9e5a04c08 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -30,7 +30,12 @@ #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Mutex.h" +#ifdef __wasi__ +# define SWIFT_CASTING_SUPPORTS_MUTEX 0 +#else +# define SWIFT_CASTING_SUPPORTS_MUTEX 1 +# include "swift/Runtime/Mutex.h" +#endif #include "swift/Runtime/Unreachable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" @@ -125,7 +130,9 @@ TypeNamePair swift::swift_getTypeName(const Metadata *type, bool qualified) { using Key = llvm::PointerIntPair; + #if SWIFT_CASTING_SUPPORTS_MUTEX static StaticReadWriteLock TypeNameCacheLock; + #endif static Lazy>> TypeNameCache; @@ -134,7 +141,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Attempt read-only lookup of cache entry. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedReadLock guard(TypeNameCacheLock); + #endif auto found = cache.find(key); if (found != cache.end()) { @@ -145,7 +154,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Read-only lookup failed to find item, we may need to create it. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedWriteLock guard(TypeNameCacheLock); + #endif // Do lookup again just to make sure it wasn't created by another // thread before we acquired the write lock. @@ -588,15 +599,6 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype, } } -// internal func _getErrorEmbeddedNSErrorIndirect( -// _ x: UnsafePointer) -> AnyObject? -#define getErrorEmbeddedNSErrorIndirect \ - MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) -SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL -id getErrorEmbeddedNSErrorIndirect(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - #endif /******************************************************************************/ @@ -1256,7 +1258,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, // Okay, we're doing a conditional cast. void *result = const_cast(swift_dynamicCastUnknownClass(object, targetType)); - assert(result == nullptr || object == result); // If the cast failed, destroy the input and return false. if (!result) { @@ -1278,14 +1279,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, /******************************** Existentials ********************************/ /******************************************************************************/ -#if SWIFT_OBJC_INTEROP -extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); - -static const WitnessTable *findErrorWitness(const Metadata *srcType) { - return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); -} -#endif - /// Perform a dynamic cast from an existential type to some kind of /// class type. static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, @@ -1298,14 +1291,6 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, auto classContainer = reinterpret_cast(src); void *obj = classContainer->Value; -#if SWIFT_OBJC_INTEROP - // If we're casting to NSError, we may need a representation change, - // so fall into the general swift_dynamicCast path. - if (targetType == getNSErrorMetadata()) { - return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj), - targetType, flags); - } -#endif return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags); } case ExistentialTypeRepresentation::Opaque: { @@ -1814,32 +1799,6 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } } -/******************************************************************************/ -/****************************** Bridging NSError ******************************/ -/******************************************************************************/ - -#if SWIFT_OBJC_INTEROP -static id dynamicCastValueToNSError(OpaqueValue *src, - const Metadata *srcType, - const WitnessTable *srcErrorWitness, - DynamicCastFlags flags) { - // Check whether there is an embedded NSError. - if (auto embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, - srcErrorWitness)) { - if (flags & DynamicCastFlags::TakeOnSuccess) - srcType->vw_destroy(src); - - return embedded; - } - - BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, - /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); - auto *error = (SwiftError *)errorBox.object; - return _swift_stdlib_bridgeErrorToNSError(error); -} - -#endif - /******************************************************************************/ /********************************* Optionals **********************************/ /******************************************************************************/ @@ -2332,26 +2291,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, // Casts to class type. case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: -#if SWIFT_OBJC_INTEROP - // If the destination type is an NSError or NSObject, and the source type - // is an Error, then the cast can succeed by NSError bridging. - if (targetType == getNSErrorMetadata() || - targetType == getNSObjectMetadata()) { - // Don't rebridge if the source is already some kind of NSError. - if (srcType->isAnyClass() - && swift_dynamicCastObjCClass(*reinterpret_cast(src), - static_cast(targetType)->Class)) - return _succeed(dest, src, srcType, flags); - if (auto srcErrorWitness = findErrorWitness(srcType)) { - auto error = dynamicCastValueToNSError(src, srcType, - srcErrorWitness, flags); - *reinterpret_cast(dest) = error; - return true; - } - } - LLVM_FALLTHROUGH; -#endif - case MetadataKind::ForeignClass: switch (srcType->getKind()) { case MetadataKind::Class: @@ -2387,6 +2326,21 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, srcBridgeWitness, flags); } + +#if SWIFT_OBJC_INTEROP + // If the destination type is an NSError or NSObject, and the source type + // is an Error, then the cast can succeed by NSError bridging. + if (targetType == getNSErrorMetadata() || + targetType == getNSObjectMetadata()) { + if (auto srcErrorWitness = findErrorWitness(srcType)) { + auto error = dynamicCastValueToNSError(src, srcType, + srcErrorWitness, flags); + *reinterpret_cast(dest) = error; + return true; + } + } +#endif + return _fail(src, srcType, targetType, flags); } @@ -2439,7 +2393,10 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, #if SWIFT_OBJC_INTEROP // If the source is an NSError, and the target is a bridgeable // Error, try to bridge. - if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) { + auto innerFlags = flags - DynamicCastFlags::Unconditional + - DynamicCastFlags::DestroyOnFailure; + if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, + innerFlags)) { return true; } #endif @@ -2846,9 +2803,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, // Handle Errors. } else if (auto srcErrorWitness = findErrorWitness(srcType)) { // Bridge the source value to an NSError. - auto box = swift_allocError(srcType, srcErrorWitness, src, consume) - .object; - return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box); + auto flags = consume ? DynamicCastFlags::TakeOnSuccess + : DynamicCastFlags::Default; + return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags); } // Fall back to boxing. diff --git a/stdlib/public/runtime/CompatibilityOverride.cpp b/stdlib/public/runtime/CompatibilityOverride.cpp index 3eebb8581ff24..7723eaad3a439 100644 --- a/stdlib/public/runtime/CompatibilityOverride.cpp +++ b/stdlib/public/runtime/CompatibilityOverride.cpp @@ -49,7 +49,12 @@ static_assert(std::is_pod::value, static OverrideSection *getOverrideSectionPtr() { static OverrideSection *OverrideSectionPtr; static swift_once_t Predicate; + // WebAssembly: hack +#ifdef __wasm__ + swift_once_real(&Predicate, [](void *) { +#else swift_once(&Predicate, [](void *) { +#endif size_t Size; OverrideSectionPtr = static_cast( lookupSection("__DATA", "__swift52_hooks", &Size)); diff --git a/stdlib/public/runtime/CompatibilityOverride.h b/stdlib/public/runtime/CompatibilityOverride.h index abc2eea227424..ad79a27273d51 100644 --- a/stdlib/public/runtime/CompatibilityOverride.h +++ b/stdlib/public/runtime/CompatibilityOverride.h @@ -39,7 +39,7 @@ namespace swift { Override_ ## name getOverride_ ## name(); #include "CompatibilityOverride.def" - +#ifndef __wasm__ /// Used to define an override point. The override point #defines the appropriate /// OVERRIDE macro from CompatibilityOverride.def to this macro, then includes /// the file to generate the override points. The original implementation of the @@ -55,6 +55,20 @@ namespace swift { return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ return swift_ ## name ## Impl namedArgs; \ } +#else +// WebAssembly: hack: change to swift_once_real +#define COMPATIBILITY_OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + attrs ccAttrs ret namespace swift_ ## name typedArgs { \ + static Override_ ## name Override; \ + static swift_once_t Predicate; \ + swift_once_real(&Predicate, [](void *) { \ + Override = getOverride_ ## name(); \ + }, nullptr); \ + if (Override != nullptr) \ + return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ + return swift_ ## name ## Impl namedArgs; \ + } +#endif } /* end namespace swift */ diff --git a/stdlib/public/runtime/ErrorObject.h b/stdlib/public/runtime/ErrorObject.h index b9f211a46d2a1..5e0b702cad5d2 100644 --- a/stdlib/public/runtime/ErrorObject.h +++ b/stdlib/public/runtime/ErrorObject.h @@ -253,6 +253,17 @@ Class getNSErrorClass(); /// Get the NSError metadata. const Metadata *getNSErrorMetadata(); +/// Find the witness table for the conformance of the given type to the +/// Error protocol, or return nullptr if it does not conform. +const WitnessTable *findErrorWitness(const Metadata *srcType); + +/// Dynamically cast a value whose conformance to the Error protocol is known +/// into an NSError instance. +id dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags); + #endif SWIFT_RUNTIME_STDLIB_SPI @@ -263,4 +274,15 @@ const size_t _swift_lldb_sizeof_SwiftError; } // namespace swift +#if SWIFT_OBJC_INTEROP +// internal func _getErrorEmbeddedNSErrorIndirect( +// _ x: UnsafePointer) -> AnyObject? +#define getErrorEmbeddedNSErrorIndirect \ + MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +id getErrorEmbeddedNSErrorIndirect(const swift::OpaqueValue *error, + const swift::Metadata *T, + const swift::WitnessTable *Error); +#endif + #endif diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index b47e6bcb71d4e..941e839da3cc1 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -178,6 +178,31 @@ - (BOOL)isEqual:(id)other { swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass())); } +extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); + +const WitnessTable *swift::findErrorWitness(const Metadata *srcType) { + return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); +} + +id swift::dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags) { + // Check whether there is an embedded NSError. + if (id embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, + srcErrorWitness)) { + if (flags & DynamicCastFlags::TakeOnSuccess) + srcType->vw_destroy(src); + + return embedded; + } + + BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, + /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); + auto *error = (SwiftError *)errorBox.object; + return _swift_stdlib_bridgeErrorToNSError(error); +} + static Class getAndBridgeSwiftNativeNSErrorClass() { Class nsErrorClass = swift::getNSErrorClass(); Class ourClass = [__SwiftNativeNSError class]; diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index 6e202434750f0..48201d54d6a2a 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -14,7 +14,7 @@ // //===----------------------------------------------------------------------===// -#if defined(__CYGWIN__) || defined(__HAIKU__) +#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__wasi__) #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 0 #else #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 1 diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index dfff68f105f0f..59acc280dfadf 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -24,7 +24,9 @@ #include // Pick a return-address strategy -#if __GNUC__ +#if defined(__wasm__) +#define get_return_address() ((void*) 0) +#elif __GNUC__ #define get_return_address() __builtin_return_address(0) #elif _MSC_VER #include @@ -36,7 +38,11 @@ using namespace swift; +#ifdef __wasm__ +bool swift::_swift_disableExclusivityChecking = true; +#else bool swift::_swift_disableExclusivityChecking = false; +#endif static const char *getAccessName(ExclusivityFlags flags) { switch (flags) { diff --git a/stdlib/public/runtime/Heap.cpp b/stdlib/public/runtime/Heap.cpp index 46e133a7b1d17..a241d153299e3 100644 --- a/stdlib/public/runtime/Heap.cpp +++ b/stdlib/public/runtime/Heap.cpp @@ -42,6 +42,11 @@ using namespace swift; #elif defined(_WIN32) # define MALLOC_ALIGN_MASK 7 +#elif defined(__wasi__) +// Musl malloc is 4*sizeof(size_t), so 16 bytes on 32-bit? +// For some reason the unknown alignment code fails because std::max isn't constexpr? +# define MALLOC_ALIGN_MASK 15 + #else // Unknown alignment, but the standard requires alignment suitable for the largest // standard types. diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 712c0afca7fc7..103e3aee87175 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -151,7 +151,13 @@ swift::swift_initStaticObject(HeapMetadata const *metadata, // refcount to 1 while another thread already incremented it - and would // decrement it to 0 afterwards. InitStaticObjectContext Ctx = { object, metadata }; +#ifdef __wasm__ + // WebAssembly: hack: swift_once has been modified to take a function pointer without a parameter. + // so use the _real version that does have a parameter + swift_once_real(token, initStaticObjectWithContext, &Ctx); +#else swift_once(token, initStaticObjectWithContext, &Ctx); +#endif return object; } diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp index a6f6d906f98e6..09f0251f12bb6 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ b/stdlib/public/runtime/ImageInspectionCOFF.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(__ELF__) && !defined(__MACH__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__wasm__) #include "ImageInspection.h" #include "ImageInspectionCOFF.h" diff --git a/stdlib/public/runtime/ImageInspectionCOFF.h b/stdlib/public/runtime/ImageInspectionCOFF.h index c76a33e25d0c4..b5e54c46687db 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.h +++ b/stdlib/public/runtime/ImageInspectionCOFF.h @@ -19,7 +19,7 @@ #ifndef SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H #define SWIFT_RUNTIME_IMAGEINSPECTIONCOFF_H -#if !defined(__ELF__) && !defined(__MACH__) +#if !defined(__ELF__) && !defined(__MACH__) && !defined(__wasm__) #include "../SwiftShims/Visibility.h" #include diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index 331ee3614f54f..9c0fe4d949dcf 100644 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ b/stdlib/public/runtime/ImageInspectionELF.cpp @@ -18,11 +18,13 @@ /// //===----------------------------------------------------------------------===// -#if defined(__ELF__) +#if defined(__ELF__) || defined(__wasm__) #include "ImageInspection.h" #include "ImageInspectionELF.h" +#ifndef __wasm__ #include +#endif using namespace swift; @@ -132,6 +134,7 @@ void swift_addNewDSOImage(const void *addr) { } int swift::lookupSymbol(const void *address, SymbolInfo *info) { +#ifndef __wasm__ Dl_info dlinfo; if (dladdr(address, &dlinfo) == 0) { return 0; @@ -142,6 +145,9 @@ int swift::lookupSymbol(const void *address, SymbolInfo *info) { info->symbolName.reset(dlinfo.dli_sname); info->symbolAddress = dlinfo.dli_saddr; return 1; +#else + return 0; +#endif } // This is only used for backward deployment hooks, which we currently only support for diff --git a/stdlib/public/runtime/ImageInspectionELF.h b/stdlib/public/runtime/ImageInspectionELF.h index afa2ee7150603..6b120897fc926 100644 --- a/stdlib/public/runtime/ImageInspectionELF.h +++ b/stdlib/public/runtime/ImageInspectionELF.h @@ -21,7 +21,7 @@ #define SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING "swift_reflection_metadata_magic_string" -#if defined(__ELF__) +#if defined(__ELF__) || defined(__wasm__) #include "../SwiftShims/Visibility.h" #include diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 66f1dc053d182..23b4ecce1e370 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -44,7 +44,10 @@ #else #include #include +// WASI doesn't have dynamic linking yet +#ifndef __wasi__ #include +#endif //__wasi #endif #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" @@ -2172,7 +2175,10 @@ namespace { #if __POINTER_WIDTH__ == 64 uint32_t Reserved; #endif - const uint8_t *IvarLayout; + union { + const uint8_t *IvarLayout; + ClassMetadata *NonMetaClass; + }; const char *Name; const void *MethodList; const void *ProtocolList; @@ -2197,7 +2203,7 @@ static inline ClassROData *getROData(ClassMetadata *theClass) { return (ClassROData*)(theClass->Data & ~uintptr_t(SWIFT_CLASS_IS_SWIFT_MASK)); } -static void initGenericClassObjCName(ClassMetadata *theClass) { +static char *copyGenericClassObjCName(ClassMetadata *theClass) { // Use the remangler to generate a mangled name from the type metadata. Demangle::StackAllocatedDemangler<4096> Dem; @@ -2230,11 +2236,54 @@ static void initGenericClassObjCName(ClassMetadata *theClass) { } else { fullNameBuf[string.size()] = '\0'; } + return fullNameBuf; +} +static void initGenericClassObjCName(ClassMetadata *theClass) { auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); - getROData(theClass)->Name = fullNameBuf; - getROData(theMetaclass)->Name = fullNameBuf; + char *name = copyGenericClassObjCName(theClass); + getROData(theClass)->Name = name; + getROData(theMetaclass)->Name = name; +} + +static bool installLazyClassNameHook() { +#if !OBJC_SETHOOK_LAZYCLASSNAMER_DEFINED + using objc_hook_lazyClassNamer = + const char * _Nullable (*)(_Nonnull Class cls); + auto objc_setHook_lazyClassNamer = + (void (*)(objc_hook_lazyClassNamer, objc_hook_lazyClassNamer *)) + dlsym(RTLD_NEXT, "objc_setHook_lazyClassNamer"); +#endif + + static objc_hook_lazyClassNamer oldHook; + auto myHook = [](Class theClass) -> const char * { + ClassMetadata *metadata = (ClassMetadata *)theClass; + if (metadata->isTypeMetadata()) + return copyGenericClassObjCName(metadata); + return oldHook(theClass); + }; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (objc_setHook_lazyClassNamer == nullptr) + return false; + objc_setHook_lazyClassNamer(myHook, &oldHook); +#pragma clang diagnostic pop + + return true; +} + +static void setUpGenericClassObjCName(ClassMetadata *theClass) { + bool supportsLazyNames = SWIFT_LAZY_CONSTANT(installLazyClassNameHook()); + if (supportsLazyNames) { + getROData(theClass)->Name = nullptr; + auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); + getROData(theMetaclass)->Name = nullptr; + getROData(theMetaclass)->NonMetaClass = theClass; + } else { + initGenericClassObjCName(theClass); + } } #endif @@ -2488,7 +2537,7 @@ initGenericObjCClass(ClassMetadata *self, size_t numFields, const TypeLayout * const *fieldTypes, size_t *fieldOffsets) { // If the class is generic, we need to give it a name for Objective-C. - initGenericClassObjCName(self); + setUpGenericClassObjCName(self); ClassROData *rodata = getROData(self); @@ -3862,6 +3911,13 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff return refAndValueAddr.buffer; } +template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const { + auto *vwt = getValueWitnesses(); + if (vwt->isValueInline()) + return; + swift_deallocBox(reinterpret_cast(buffer->PrivateData[0])); +} + template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const { auto *vwt = getValueWitnesses(); if (vwt->isValueInline()) @@ -4577,6 +4633,14 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness( template static Result performOnMetadataCache(const Metadata *metadata, Callbacks &&callbacks) { + // TODO: Once more than just structs have canonical statically specialized + // metadata, calling an updated + // isCanonicalStaticallySpecializedGenericMetadata would entail + // dyn_casting to the same type more than once. Avoid that by combining + // that function's implementation with the dyn_casts below. + if (metadata->isCanonicalStaticallySpecializedGenericMetadata()) + return std::move(callbacks).forOtherMetadata(metadata); + // Handle different kinds of type that can delay their metadata. const TypeContextDescriptor *description; if (auto classMetadata = dyn_cast(metadata)) { @@ -5158,6 +5222,14 @@ bool Metadata::satisfiesClassConstraint() const { return isAnyClass(); } +template <> +bool Metadata::isCanonicalStaticallySpecializedGenericMetadata() const { + if (auto *metadata = dyn_cast(this)) + return metadata->isCanonicalStaticallySpecializedGenericMetadata(); + + return false; +} + #if !NDEBUG static bool referencesAnonymousContext(Demangle::Node *node) { if (node->getKind() == Demangle::Node::Kind::AnonymousContext) diff --git a/stdlib/public/runtime/MutexPThread.cpp b/stdlib/public/runtime/MutexPThread.cpp index 62e718c16abaa..92db58226cd09 100644 --- a/stdlib/public/runtime/MutexPThread.cpp +++ b/stdlib/public/runtime/MutexPThread.cpp @@ -15,7 +15,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(_WIN32) +#if !defined(_WIN32) && !defined(__wasi__) #include "swift/Runtime/Mutex.h" #include "swift/Runtime/Debug.h" diff --git a/stdlib/public/runtime/MutexWASI.cpp b/stdlib/public/runtime/MutexWASI.cpp new file mode 100644 index 0000000000000..6288ecce03449 --- /dev/null +++ b/stdlib/public/runtime/MutexWASI.cpp @@ -0,0 +1,25 @@ +//===--- MutexWin32.cpp - -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Mutex, ConditionVariable, Read/Write lock, and Scoped lock implementations +// using Windows Slim Reader/Writer Locks and Conditional Variables. +// +//===----------------------------------------------------------------------===// + +#if defined(__wasi__) +#include "swift/Runtime/Mutex.h" + +using namespace swift; + +void ConditionPlatformHelper::wait(void* &condition, + void* &mutex) {} +#endif diff --git a/stdlib/public/runtime/Once.cpp b/stdlib/public/runtime/Once.cpp index 57282590cbd06..7554d5d4ae751 100644 --- a/stdlib/public/runtime/Once.cpp +++ b/stdlib/public/runtime/Once.cpp @@ -52,7 +52,28 @@ void swift::swift_once(swift_once_t *predicate, void (*fn)(void *), dispatch_once_f(predicate, context, fn); #elif defined(__CYGWIN__) _swift_once_f(predicate, context, fn); +#elif defined(__wasm__) + // WebAssembly: hack: Swift compiler passes in a fn that doesn't take a parameter, + // which is invalid in WebAssembly. So swift_once casts the function. + // The correct way to fix this is to change + // SILGenModule::emitLazyGlobalInitializer + // but this is OK as a proof of concept. + // Keep a copy of the unmodified swift_once function below. + std::call_once(*predicate, [fn, context]() { ((void (*)())fn)(); }); #else std::call_once(*predicate, [fn, context]() { fn(context); }); #endif } + +#ifdef __wasm__ +void swift::swift_once_real(swift_once_t *predicate, void (*fn)(void *), + void *context) { +#if defined(__APPLE__) + dispatch_once_f(predicate, context, fn); +#elif defined(__CYGWIN__) + _swift_once_f(predicate, context, fn); +#else + std::call_once(*predicate, [fn, context]() { fn(context); }); +#endif +} +#endif diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 9bd4a808876fa..11b82d09c3578 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -50,8 +50,10 @@ class TypeReferenceOwnership { #define REF_STORAGE(Name, ...) \ void set##Name() { Data |= Name; } \ - bool is##Name() const { return Data & Name; } + bool is##Name() const { return Data == Name; } #include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return Data == 0; } }; /// Type information consists of metadata and its ownership info, @@ -76,9 +78,11 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } - bool isWeak() const { return ReferenceOwnership.isWeak(); } - bool isUnowned() const { return ReferenceOwnership.isUnowned(); } - bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); } +#define REF_STORAGE(Name, ...) \ + bool is##Name() const { return ReferenceOwnership.is##Name(); } +#include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return ReferenceOwnership.isStrong(); } TypeReferenceOwnership getReferenceOwnership() const { return ReferenceOwnership; diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 310bd403d3b29..32dd7e7398cab 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -89,6 +89,29 @@ - (id)debugQuickLookObject; namespace { +class FieldType { + const Metadata *type; + bool indirect; + TypeReferenceOwnership referenceOwnership; +public: + + constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { } + constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { } + + static constexpr FieldType untypedEnumCase(bool indirect) { + FieldType type{}; + type.indirect = indirect; + return type; + } + const Metadata *getType() const { return type; } + const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; } + bool isIndirect() const { return indirect; } + void setIndirect(bool value) { indirect = value; } + void setReferenceOwnership(TypeReferenceOwnership newOwnership) { + referenceOwnership = newOwnership; + } +}; + /// The layout of Any. using Any = OpaqueExistentialContainer; @@ -123,58 +146,63 @@ - (id)debugQuickLookObject; return std::make_tuple(T, Value); } -static bool loadSpecialReferenceStorage(OpaqueValue *fieldData, - const FieldType fieldType, - Any *outValue) { - // isWeak() implies a reference type via Sema. - if (!fieldType.isWeak()) - return false; - - auto type = fieldType.getType(); +static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { assert(type->getKind() == MetadataKind::Optional); + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} - auto *weakField = reinterpret_cast(fieldData); - auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField); - - // Now that we have a strong reference, we need to create a temporary buffer - // from which to copy the whole value, which might be a native class-bound - // existential, which means we also need to copy n witness tables, for - // however many protocols are in the protocol composition. For example, if we - // are copying a: - // weak var myWeakProperty : (Protocol1 & Protocol2)? - // then we need to copy three values: - // - the instance - // - the witness table for Protocol1 - // - the witness table for Protocol2 - - auto *weakContainer = - reinterpret_cast(fieldData); - - // Create a temporary existential where we can put the strong reference. - // The allocateBuffer value witness requires a ValueBuffer to own the - // allocated storage. - ValueBuffer temporaryBuffer; - - auto *temporaryValue = reinterpret_cast( - type->allocateBufferIn(&temporaryBuffer)); - - // Now copy the entire value out of the parent, which will include the - // witness tables. - temporaryValue->Value = strongValue; - auto valueWitnessesSize = type->getValueWitnesses()->getSize() - - sizeof(WeakClassExistentialContainer); - memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(), - valueWitnessesSize); - - outValue->Type = type; - auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer); - type->vw_initializeWithCopy(opaqueValueAddr, - reinterpret_cast(temporaryValue)); - - type->deallocateBufferIn(&temporaryBuffer); - swift_unknownObjectRelease(strongValue); - - return true; +static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} + +static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + // Also known as "unowned(unsafe)". + // This is simpler than the unowned/weak cases because unmanaged + // references are fundamentally the same as strong ones, so we + // can use the regular strong reference support that already + // knows how to handle existentials and Obj-C references. + type->vw_initializeWithCopy(destContainer, fieldData); +} + +static AnyReturn copyFieldContents(OpaqueValue *fieldData, + const FieldType fieldType) { + Any outValue; + auto *type = fieldType.getType(); + outValue.Type = type; + auto ownership = fieldType.getReferenceOwnership(); + auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer); + + if (ownership.isStrong()) { + type->vw_initializeWithCopy(destContainer, fieldData); + } + + // Generate a conditional clause for every known ownership type. + // If this causes errors, it's because someone added a new ownership type + // to ReferenceStorage.def and missed some related updates. +#define REF_STORAGE(Name, ...) \ + else if (ownership.is##Name()) { \ + copy##Name##FieldContents(destContainer, type, fieldData); \ + } +#include "swift/AST/ReferenceStorage.def" + + else { + // The field was declared with a reference type we don't understand. + warning(0, "Value with unrecognized reference type is reflected as ()"); + // Clean up the buffer allocated above + type->deallocateBoxForExistentialIn(&outValue.Buffer); + // Return an existential containing Void + outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING); + } + + return AnyReturn(outValue); } @@ -310,11 +338,7 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); - return {"unknown", - FieldType() - .withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING)) - .withIndirect(false) - .withWeak(false)}; + return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; }; auto *baseDesc = base->getTypeContextDescriptor(); @@ -325,14 +349,13 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { if (!fields) return failedToFindMetadata(); - const FieldDescriptor &descriptor = *fields; - auto &field = descriptor.getFields()[index]; + auto &field = fields->getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) - return {name, FieldType().withIndirect(field.isIndirectCase())}; + return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); @@ -360,10 +383,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { (int)typeName.size(), typeName.data()); } - return {name, FieldType() - .withType(typeInfo.getMetadata()) - .withIndirect(field.isIndirectCase()) - .withWeak(typeInfo.isWeak())}; + auto fieldType = FieldType(typeInfo.getMetadata()); + fieldType.setIndirect(field.isIndirectCase()); + fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); + return {name, fieldType}; } // Implementation for structs. @@ -397,7 +420,6 @@ AnyReturn subscript(intptr_t i, const char **outName, // Load the offset from its respective vector. auto fieldOffset = Struct->getFieldOffsets()[i]; - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -409,15 +431,7 @@ AnyReturn subscript(intptr_t i, const char **outName, auto *bytes = reinterpret_cast(value); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } }; @@ -559,7 +573,6 @@ AnyReturn subscript(intptr_t i, const char **outName, #endif } - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -571,15 +584,7 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } #if SWIFT_OBJC_INTEROP diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 95ba09346ba44..6a2393e033e5e 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -35,6 +35,7 @@ #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" #include "CompatibilityOverride.h" +#include "ErrorObject.h" #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" @@ -1098,6 +1099,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + return nullptr; } @@ -1114,6 +1129,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + Class sourceType = object_getClass(id_const_cast(object)); swift_dynamicCastFailure(reinterpret_cast(sourceType), targetType); diff --git a/stdlib/public/runtime/SwiftRT-WASM.cpp b/stdlib/public/runtime/SwiftRT-WASM.cpp new file mode 100644 index 0000000000000..24dc7130def4d --- /dev/null +++ b/stdlib/public/runtime/SwiftRT-WASM.cpp @@ -0,0 +1,82 @@ +//===--- SwiftRT-WASM.cpp --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ImageInspectionELF.h" + +#include + +// Link start/stop symbols weakly to link them if they aren't synthesized by the linker. +#define DECLARE_SWIFT_SECTION(name) \ + __attribute__((__visibility__("hidden"),__aligned__(1),weak)) extern const char __start_##name; \ + __attribute__((__visibility__("hidden"),__aligned__(1),weak)) extern const char __stop_##name; + +extern "C" { +DECLARE_SWIFT_SECTION(swift5_protocols) +DECLARE_SWIFT_SECTION(swift5_protocol_conformances) +DECLARE_SWIFT_SECTION(swift5_type_metadata) + +DECLARE_SWIFT_SECTION(swift5_typeref) +DECLARE_SWIFT_SECTION(swift5_reflstr) +DECLARE_SWIFT_SECTION(swift5_fieldmd) +DECLARE_SWIFT_SECTION(swift5_assocty) +DECLARE_SWIFT_SECTION(swift5_replace) +DECLARE_SWIFT_SECTION(swift5_replac2) +DECLARE_SWIFT_SECTION(swift5_builtin) +DECLARE_SWIFT_SECTION(swift5_capture) +} + +#undef DECLARE_SWIFT_SECTION + +namespace { +static swift::MetadataSections sections{}; +} + +__attribute__((__constructor__)) +static void swift_image_constructor() { +#define SWIFT_SECTION_RANGE(name) \ + { reinterpret_cast(&__start_##name), \ + static_cast(&__stop_##name - &__start_##name) } + + sections = { + swift::CurrentSectionMetadataVersion, + 0, + + nullptr, + nullptr, + + SWIFT_SECTION_RANGE(swift5_protocols), + SWIFT_SECTION_RANGE(swift5_protocol_conformances), + SWIFT_SECTION_RANGE(swift5_type_metadata), + + SWIFT_SECTION_RANGE(swift5_typeref), + SWIFT_SECTION_RANGE(swift5_reflstr), + SWIFT_SECTION_RANGE(swift5_fieldmd), + SWIFT_SECTION_RANGE(swift5_assocty), + SWIFT_SECTION_RANGE(swift5_replace), + SWIFT_SECTION_RANGE(swift5_replac2), + SWIFT_SECTION_RANGE(swift5_builtin), + SWIFT_SECTION_RANGE(swift5_capture), + }; + +#undef SWIFT_SECTION_RANGE + + swift_addNewDSOImage(§ions); +} + +static __attribute__((__used__)) +__attribute__((__section__(".note.swift_reflection_metadata"))) +__attribute__((__aligned__(1))) +struct { + const char MagicString[sizeof(SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING)]; + const swift::MetadataSections *Sections; +} __attribute__((__packed__)) +Note = {SWIFT_REFLECTION_METADATA_ELF_NOTE_MAGIC_STRING, §ions}; diff --git a/stdlib/public/runtime/ThreadLocalStorage.h b/stdlib/public/runtime/ThreadLocalStorage.h index 82e2457b24f8a..4e62b76b1d74e 100644 --- a/stdlib/public/runtime/ThreadLocalStorage.h +++ b/stdlib/public/runtime/ThreadLocalStorage.h @@ -81,6 +81,8 @@ typedef int __swift_thread_key_t; typedef unsigned long __swift_thread_key_t; # elif defined(__HAIKU__) typedef int __swift_thread_key_t; +# elif defined(__wasi__) +typedef unsigned int __swift_thread_key_t; # else typedef unsigned long __swift_thread_key_t; # endif @@ -96,7 +98,13 @@ static_assert(std::is_same<__swift_thread_key_t, DWORD>::value, # define SWIFT_THREAD_KEY_CREATE _stdlib_thread_key_create # define SWIFT_THREAD_GETSPECIFIC FlsGetValue # define SWIFT_THREAD_SETSPECIFIC(key, value) (FlsSetValue(key, value) == FALSE) - +# elif defined(__wasi__) +int wasi_polyfill_pthread_key_create(__swift_thread_key_t *key, void (*destructor)(void*)); +void *wasi_polyfill_pthread_getspecific(__swift_thread_key_t key); +int wasi_polyfill_pthread_setspecific(__swift_thread_key_t key, const void *value); +# define SWIFT_THREAD_KEY_CREATE wasi_polyfill_pthread_key_create +# define SWIFT_THREAD_GETSPECIFIC wasi_polyfill_pthread_getspecific +# define SWIFT_THREAD_SETSPECIFIC wasi_polyfill_pthread_setspecific # else // Otherwise use the pthread API. # include diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 083deaa4747a1..b54ede8c61f66 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -23,7 +23,7 @@ #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__wasi__) #include #endif diff --git a/stdlib/public/stubs/Random.cpp b/stdlib/public/stubs/Random.cpp index bb69f7e7793d1..e099d9c5e43ee 100644 --- a/stdlib/public/stubs/Random.cpp +++ b/stdlib/public/stubs/Random.cpp @@ -42,6 +42,10 @@ #include "swift/Runtime/Mutex.h" #include "../SwiftShims/Random.h" +#ifdef __wasi__ +#include // std::min +#endif + #if defined(__APPLE__) SWIFT_RUNTIME_STDLIB_API @@ -88,7 +92,7 @@ void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) { if (getrandom_available) { actual_nbytes = WHILE_EINTR(syscall(__NR_getrandom, buf, nbytes, 0)); } -#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__)) +#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__) || defined(__wasi__)) __swift_size_t getentropy_nbytes = std::min(nbytes, __swift_size_t{256}); if (0 == getentropy(buf, getentropy_nbytes)) { diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 55bada6a1db92..d8416c62f1733 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -26,7 +26,7 @@ #define NOMINMAX #include #else -#if !defined(__HAIKU__) +#if !defined(__HAIKU__) && !defined(__wasi__) #include #else #include @@ -67,7 +67,7 @@ static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) { #define strtod_l swift_strtod_l #define strtof_l swift_strtof_l #endif -#elif defined(__linux__) +#elif defined(__linux__) || defined(__wasi__) #include #else #include @@ -503,6 +503,8 @@ const char *swift::_swift_stdlib_strtof_clocale( void swift::_swift_stdlib_flockfile_stdout() { #if defined(_WIN32) _lock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet #else flockfile(stdout); #endif @@ -511,6 +513,8 @@ void swift::_swift_stdlib_flockfile_stdout() { void swift::_swift_stdlib_funlockfile_stdout() { #if defined(_WIN32) _unlock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet #else funlockfile(stdout); #endif diff --git a/stdlib/public/stubs/ThreadLocalStorage.cpp b/stdlib/public/stubs/ThreadLocalStorage.cpp index 50168f955bc6c..4e855cb593b72 100644 --- a/stdlib/public/stubs/ThreadLocalStorage.cpp +++ b/stdlib/public/stubs/ThreadLocalStorage.cpp @@ -25,6 +25,26 @@ void *_stdlib_createTLS(void); #if !SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC || (defined(_WIN32) && !defined(__CYGWIN__)) +#if defined(__wasi__) +#define STUB() do { /* fprintf(stderr, "%s is unsupported on WASI environment\n", __func__);*/ abort(); } while(0) +void wasi_polyfill_call_once(int *flag, void *context, void (*func)(void *)) + { + switch (*flag) { + case 0: + func(context); + *flag = 1; + return; + case 1: + return; + default: + STUB(); + } + } +int wasi_polyfill_pthread_key_create(__swift_thread_key_t *key, void (*destructor)(void*)) { STUB(); } +void *wasi_polyfill_pthread_getspecific(__swift_thread_key_t key) { STUB(); } +int wasi_polyfill_pthread_setspecific(__swift_thread_key_t key, const void *value) { STUB(); } +#endif + static void #if defined(_M_IX86) __stdcall diff --git a/stdlib/tools/swift-reflection-test/swift-reflection-test.c b/stdlib/tools/swift-reflection-test/swift-reflection-test.c index dfcfe544c1078..e454e0dcd493b 100644 --- a/stdlib/tools/swift-reflection-test/swift-reflection-test.c +++ b/stdlib/tools/swift-reflection-test/swift-reflection-test.c @@ -27,7 +27,7 @@ #include #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__wasi__) #include #elif defined(_WIN32) #include diff --git a/test/AutoDiff/ModuleInterface/differentiation.swift b/test/AutoDiff/ModuleInterface/differentiation.swift index 6b3a791fb9ca3..35ee0f9067891 100644 --- a/test/AutoDiff/ModuleInterface/differentiation.swift +++ b/test/AutoDiff/ModuleInterface/differentiation.swift @@ -6,3 +6,6 @@ public func a(f: @differentiable (Float) -> Float) {} public func b(f: @differentiable(linear) (Float) -> Float) {} // CHECK: public func b(f: @differentiable(linear) (Swift.Float) -> Swift.Float) + +public func c(f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: public func c(f: @differentiable (Swift.Float, @noDerivative Swift.Float) -> Swift.Float) diff --git a/test/AutoDiff/Parse/derivative_attr_parse.swift b/test/AutoDiff/Parse/derivative_attr_parse.swift index 8d144e5e3779e..5bdf00cfb303d 100644 --- a/test/AutoDiff/Parse/derivative_attr_parse.swift +++ b/test/AutoDiff/Parse/derivative_attr_parse.swift @@ -28,8 +28,7 @@ func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { /// Bad -// expected-error @+3 {{expected an original function name}} -// expected-error @+2 {{expected ')' in 'derivative' attribute}} +// expected-error @+2 {{expected an original function name}} // expected-error @+1 {{expected declaration}} @derivative(of: 3) func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { diff --git a/test/AutoDiff/Parse/differentiable_attr_parse.swift b/test/AutoDiff/Parse/differentiable_attr_parse.swift index 354d385ba3604..eb94ff59728ab 100644 --- a/test/AutoDiff/Parse/differentiable_attr_parse.swift +++ b/test/AutoDiff/Parse/differentiable_attr_parse.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -parse -verify %s +// TODO(TF-1021): Remove "deprecated 'jvp:' and 'vjp:' argument" warnings. + /// Good struct Foo { @@ -7,21 +9,31 @@ struct Foo { var x: Float } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(vjp: foo(_:_:)) // okay func bar(_ x: Float, _: Float) -> Float { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(vjp: foo(_:_:) where T : FloatingPoint) // okay func bar(_ x: T, _: T) -> T { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self, x, y), vjp: foo(_:_:)) // okay func bar(_ x: Float, _ y: Float) -> Float { return 1 + x } +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:)) // okay func bar(_ x: Float, _ y: Float) -> Float { return 1 + x @@ -55,6 +67,7 @@ func playWellWithOtherAttrs(_ x: Float, _: Float) -> Float { } @_transparent +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} @differentiable(wrt: (self), vjp: _vjpSquareRoot) // okay public func squareRoot() -> Self { var lhs = self @@ -99,82 +112,104 @@ func two(x: Float, y: Float) -> Float { /// Bad -@differentiable(3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(3) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(foo(_:_:)) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:), 3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(vjp: foo(_:_:), 3) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(wrt: (x), foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: (x), foo(_:_:)) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(wrt: x, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, y) func bar(_ x: Float, _ y: Float) -> Float { return 1 + x } -@differentiable(wrt: 0, 1) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, 1) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(wrt: 0, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, y) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(wrt: 0,) // expected-error {{unexpected ',' separator}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(wrt: 0,) func two(x: Float, y: Float) -> Float { return x + y } -@differentiable(vjp: foo(_:_:) // expected-error {{expected ')' in 'differentiable' attribute}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ')' in 'differentiable' attribute}} +@differentiable(vjp: foo(_:_:) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:) where T) // expected-error {{expected ':' or '==' to indicate a conformance or same-type requirement}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ':' or '==' to indicate a conformance or same-type requirement}} +@differentiable(vjp: foo(_:_:) where T) func bar(_ x: T, _: T) -> T { return 1 + x } -@differentiable(,) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(,) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:),) // expected-error {{unexpected ',' separator}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:),) func bar(_ x: Float, _: Float) -> Float { return 1 + x } -@differentiable(vjp: foo(_:_:), where T) // expected-error {{unexpected ',' separator}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:), where T) func bar(_ x: T, _: T) -> T { return 1 + x } -@differentiable(wrt: x, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear) func slope4(_ x: Float) -> Float { return 4 * x } -@differentiable(wrt: x, linear, vjp: const5) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear, vjp: const5) func slope5(_ x: Float) -> Float { return 5 * x } -@differentiable(wrt: x, vjp: const6, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, vjp: const6, linear) func slope5(_ x: Float) -> Float { return 6 * x } diff --git a/test/AutoDiff/Parse/differentiable_func_type.swift b/test/AutoDiff/Parse/differentiable_func_type.swift index 7ef91d3524fb9..e163a246a4fba 100644 --- a/test/AutoDiff/Parse/differentiable_func_type.swift +++ b/test/AutoDiff/Parse/differentiable_func_type.swift @@ -8,12 +8,25 @@ let b: @differentiable(linear) (Float) -> Float // okay // CHECK: (pattern_named 'b' // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) -let c: @differentiable (Float) throws -> Float // okay +let c: @differentiable (Float, @noDerivative Float) -> Float // okay // CHECK: (pattern_named 'c' +// CHECK-NEXT: (type_attributed attrs=@differentiable +// CHECK-NEXT: (type_function +// CHECK-NEXT: (type_tuple +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none)) +// CHECK-NEXT: (type_attributed attrs=@noDerivative +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))) +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))))) + +let d: @differentiable (Float) throws -> Float // okay +// CHECK: (pattern_named 'd' // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} -let d: @differentiable(linear) (Float) throws -> Float // okay -// CHECK: (pattern_named 'd' +let e: @differentiable(linear) (Float) throws -> Float // okay +// CHECK: (pattern_named 'e' // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) // Generic type test. diff --git a/test/AutoDiff/Parse/transpose_attr_parse.swift b/test/AutoDiff/Parse/transpose_attr_parse.swift new file mode 100644 index 0000000000000..09f9685283896 --- /dev/null +++ b/test/AutoDiff/Parse/transpose_attr_parse.swift @@ -0,0 +1,96 @@ +// RUN: %target-swift-frontend -parse -verify %s + +/// Good + +@transpose(of: foo) +func transpose(v: Float) -> Float + +@transpose(of: foo(_:_:)) +func transpose(v: Float) -> Float + +@transpose(of: wrt, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: (0, 1)) +func transpose(v: Float) -> (Float, Float) + +@transpose(of: foo, wrt: (self, 0, 1, 2)) +func transpose(v: Float) -> (Float, Float, Float, Float) + +// Qualified declaration. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + +// Qualified declaration with specialized generic type. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + +// Qualified operator. +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float.+) +func transpose(v: Float) -> Float + +// Qualified leading-period operator (confusing). +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float..<) +func transpose(v: Float) -> Float + +// `init` keyword edge case. +@transpose(of: Swift.Float.init(_:)) +func transpose(v: Float) -> Float + +// `subscript` keyword edge case. +@transpose(of: Swift.Array.subscript(_:)) +func transpose(v: Float) -> Float + +/// Bad + +// expected-error @+2 {{expected an original function name}} +// expected-error @+1 {{expected declaration}} +@transpose(of: 3) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected label 'wrt:' in '@transpose' attribute}} +@transpose(of: foo, blah) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a colon ':' after 'wrt'}} +@transpose(of: foo, wrt) +func transpose(v: Float) -> Float + +// expected-error @+1 {{unexpected ',' separator}} +@transpose(of: foo,) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: foo, wrt: 0,) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: v) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: (0, v)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+(_:_)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+.a) +func transpose(v: Float) -> Float + +func testLocalTransposeRegistration() { + // Transpose registration can only be non-local. + // expected-error @+1 {{attribute '@transpose' can only be used in a non-local scope}} + @transpose(of: +) + func transpose(_ x: Float) -> (Float, Float) +} diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift index c3d7f9e9613bf..babb8fad87aa5 100644 --- a/test/AutoDiff/Sema/derivative_attr_type_checking.swift +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -185,6 +185,11 @@ protocol StaticMethod: Differentiable { static func generic(_ x: T) -> T } +extension StaticMethod { + static func foo(_ x: Float) -> Float { x } + static func generic(_ x: T) -> T { x } +} + extension StaticMethod { @derivative(of: foo) static func jvpFoo(x: Float) -> (value: Float, differential: (Float) -> Float) @@ -192,7 +197,8 @@ extension StaticMethod { return (x, { $0 }) } - @derivative(of: foo) + // Test qualified declaration name. + @derivative(of: StaticMethod.foo) static func vjpFoo(x: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { $0 }) } @@ -214,11 +220,16 @@ extension StaticMethod { // Test instance methods. protocol InstanceMethod: Differentiable { - // expected-note @+1 {{'foo' defined here}} func foo(_ x: Self) -> Self + func generic(_ x: T) -> Self +} + +extension InstanceMethod { + // expected-note @+1 {{'foo' defined here}} + func foo(_ x: Self) -> Self { x } // expected-note @+1 {{'generic' defined here}} - func generic(_ x: T) -> Self + func generic(_ x: T) -> Self { self } } extension InstanceMethod { @@ -229,6 +240,14 @@ extension InstanceMethod { return (x, { $0 + $1 }) } + // Test qualified declaration name. + @derivative(of: InstanceMethod.foo, wrt: x) + func jvpFooWrtX(x: Self) -> ( + value: Self, differential: (TangentVector) -> (TangentVector) + ) { + return (x, { $0 }) + } + @derivative(of: generic) func vjpGeneric(_ x: T) -> ( value: Self, pullback: (TangentVector) -> (TangentVector, T.TangentVector) @@ -276,7 +295,7 @@ func req1(_ x: T) -> T { return x } @derivative(of: req1) -func vjpReq1(_ x: T) -> ( +func vjpExtraConformanceConstraint(_ x: T) -> ( value: T, pullback: (T.TangentVector) -> T.TangentVector ) { return (x, { $0 }) @@ -286,12 +305,42 @@ func req2(_ x: T, _ y: U) -> T { return x } @derivative(of: req2) -func vjpReq2(_ x: T, _ y: U) - -> (value: T, pullback: (T) -> (T, U)) -where T == T.TangentVector, U == U.TangentVector, T: CustomStringConvertible { +func vjpExtraConformanceConstraints( _ x: T, _ y: U) -> ( + value: T, pullback: (T) -> (T, U) +) where T == T.TangentVector, U == U.TangentVector, T: CustomStringConvertible { return (x, { ($0, .zero) }) } +// Test `@derivative` declaration with extra same-type requirements. +func req3(_ x: T) -> T { + return x +} +@derivative(of: req3) +func vjpSameTypeRequirementsGenericParametersAllConcrete(_ x: T) -> ( + value: T, pullback: (T.TangentVector) -> T.TangentVector +) where T: Differentiable, T.TangentVector == Float { + return (x, { $0 }) +} + +struct Wrapper: Equatable { + var x: T + init(_ x: T) { self.x = x } +} +extension Wrapper: AdditiveArithmetic where T: AdditiveArithmetic { + static var zero: Self { .init(.zero) } + static func + (lhs: Self, rhs: Self) -> Self { .init(lhs.x + rhs.x) } + static func - (lhs: Self, rhs: Self) -> Self { .init(lhs.x - rhs.x) } +} +extension Wrapper: Differentiable where T: Differentiable, T == T.TangentVector { + typealias TangentVector = Wrapper +} +extension Wrapper where T: Differentiable, T == T.TangentVector { + @derivative(of: init(_:)) + static func vjpInit(_ x: T) -> (value: Self, pullback: (Wrapper.TangentVector) -> (T)) { + fatalError() + } +} + // Test class methods. class Super { @@ -497,27 +546,34 @@ extension HasStoredProperty { } } -// Test cross-file derivative registration. Currently unsupported. -// TODO(TF-1021): Lift this restriction. +// Test derivative registration for protocol requirements. Currently unsupported. +// TODO(TF-982): Lift this restriction and add proper support. -extension AdditiveArithmetic where Self: Differentiable { - // expected-error @+1 {{derivative not in the same file as the original function}} - @derivative(of: +) - static func vjpPlus(x: Self, y: Self) -> ( - value: Self, - pullback: (Self.TangentVector) -> (Self.TangentVector, Self.TangentVector) - ) { - return (x + y, { v in (v, v) }) +protocol ProtocolRequirementDerivative { + func requirement(_ x: Float) -> Float +} +extension ProtocolRequirementDerivative { + // NOTE: the error is misleading because `findAbstractFunctionDecl` in + // TypeCheckAttr.cpp is not setup to show customized error messages for + // invalid original function candidates. + // expected-error @+1 {{could not find function 'requirement' with expected type ' (Self) -> (Float) -> Float'}} + @derivative(of: requirement) + func vjpRequirement(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + fatalError() } } -extension FloatingPoint where Self: Differentiable, Self == Self.TangentVector { +// Test cross-file derivative registration. Currently unsupported. +// TODO(TF-1021): Lift this restriction. + +extension FloatingPoint where Self: Differentiable { // expected-error @+1 {{derivative not in the same file as the original function}} - @derivative(of: +) - static func vjpPlus(x: Self, y: Self) -> ( - value: Self, pullback: (Self) -> (Self, Self) + @derivative(of: rounded) + func vjpRounded() -> ( + value: Self, + pullback: (Self.TangentVector) -> (Self.TangentVector) ) { - return (x + y, { v in (v, v) }) + fatalError() } } diff --git a/test/AutoDiff/Sema/differentiable_features_disabled.swift b/test/AutoDiff/Sema/differentiable_features_disabled.swift index e8212a69258d8..1ba3ec05e8a80 100644 --- a/test/AutoDiff/Sema/differentiable_features_disabled.swift +++ b/test/AutoDiff/Sema/differentiable_features_disabled.swift @@ -3,6 +3,16 @@ // expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} let _: @differentiable (Float) -> Float +// expected-error @+2 {{differentiable programming is an experimental feature that is currently disabled}} +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @differentiable (Float, @noDerivative Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: (Float, @noDerivative Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @noDerivative Float + func id(_ x: Float) -> Float { return x } diff --git a/test/AutoDiff/Sema/differentiable_func_type.swift b/test/AutoDiff/Sema/differentiable_func_type.swift index 0ca2f8aa50296..0515dcc95d027 100644 --- a/test/AutoDiff/Sema/differentiable_func_type.swift +++ b/test/AutoDiff/Sema/differentiable_func_type.swift @@ -4,3 +4,24 @@ let _: @differentiable Float let _: @differentiable (Float) -> Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (Float) -> @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @differentiable (Float) -> @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (@noDerivative Float) -> Float + +// expected-error @+2 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: (Float, @noDerivative Float...) -> Float + +let _: @differentiable (@noDerivative Float, Float) -> Float + +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: @differentiable (Float, @noDerivative Float...) -> Float diff --git a/test/AutoDiff/Serialization/differentiation.swift b/test/AutoDiff/Serialization/differentiation.swift index d5f1276c3f494..3a17b5440b8a9 100644 --- a/test/AutoDiff/Serialization/differentiation.swift +++ b/test/AutoDiff/Serialization/differentiation.swift @@ -10,3 +10,6 @@ func a(_ f: @differentiable (Float) -> Float) {} func b(_ f: @differentiable(linear) (Float) -> Float) {} // CHECK: func b(_ f: @differentiable(linear) (Float) -> Float) + +func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) diff --git a/test/AutoDiff/Serialization/transpose_attr.swift b/test/AutoDiff/Serialization/transpose_attr.swift new file mode 100644 index 0000000000000..80bc7a220586b --- /dev/null +++ b/test/AutoDiff/Serialization/transpose_attr.swift @@ -0,0 +1,99 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-experimental-differentiable-programming %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/transpose_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -enable-experimental-differentiable-programming -disable-sil-linking -enable-sil-verify-all %t/transpose_attr.swiftmodule -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode +// REQUIRES: differentiable_programming + +// TODO(TF-838): Enable this test. +// Blocked by TF-830: `@transpose` attribute type-checking. +// XFAIL: * + +import _Differentiation + +// Dummy `Differentiable`-conforming type. +struct S: Differentiable & AdditiveArithmetic { + static var zero: S { S() } + static func + (_: S, _: S) -> S { S() } + static func - (_: S, _: S) -> S { S() } + typealias TangentVector = S +} + +// Test top-level functions. + +func top1(_ x: S) -> S { + x +} +// CHECK: @transpose(of: top1, wrt: 0) +@transpose(of: top1, wrt: 0) +func transposeTop1(v: S) -> S { + v +} + +func top2(_ x: T, _ i: Int, _ y: U) -> U { + y +} +// CHECK: @transpose(of: top2, wrt: (0, 2)) +@transpose(of: top2, wrt: (0, 2)) +func transposeTop2(_ int: Int, v: U) -> (T, U) +where T: Differentiable, U: Differentiable, + T == T.TangentVector, U == U.TangentVector { + (.zero, v) +} + +// Test instance methods. + +extension S { + func instanceMethod(_ other: S) -> S { + self + other + } + + // CHECK: @transpose(of: instanceMethod, wrt: 0) + @transpose(of: instanceMethod, wrt: 0) + func transposeInstanceMethod(v: S) -> (S, S) { + (v, v) + } + + // CHECK: @transpose(of: instanceMethod, wrt: self) + @transpose(of: instanceMethod, wrt: self) + func transposeInstanceMethodWrtSelf(v: S) -> (S, S) { + (v, v) + } +} + +// Test static methods. + +extension S { + static func staticMethod(x: S) -> S { + x + } + + // CHECK: @transpose(of: staticMethod, wrt: 0) + @transpose(of: staticMethod, wrt: 0) + func transposeStaticMethod(_: S.Type) -> S { + self + } +} + +// Test computed properties. +extension S { + var computedProperty: S { self } + + // CHECK: @transpose(of: computedProperty, wrt: self) + @transpose(of: computedProperty, wrt: self) + func transposeProperty() -> Self { + self + } +} + +// Test subscripts. +extension S { + subscript(x: T) -> Self { self } + + // CHECK: @transpose(of: subscript, wrt: self) + @transpose(of: subscript(_:), wrt: self) + func transposeSubscript(x: T) -> Self { + self + } +} diff --git a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 8aa4210d41c35..c49bacacb7633 100644 --- a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -26,14 +26,52 @@ func bar(_ x: wrt: (self, x, y), jvp: bar, vjp: foo(_:_:) where T : FloatingPoint) func bar<T : Numeric>(_ x: T, y: T) -> T { return 1 } -@derivative(of: -) +@derivative(of: -) func negateDerivative(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { return (-x, { v in -v }) } -@derivative(of: baz(label:_:), wrt: (x)) +@derivative(of: baz(label:_:), wrt: (x)) func bazDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) +} + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: x) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: 0) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) } diff --git a/test/AutoDiff/Syntax/round_trip_parse_gen.swift b/test/AutoDiff/Syntax/round_trip_parse_gen.swift index 9e2d4b0a9fc08..bfde3d5e04cfd 100644 --- a/test/AutoDiff/Syntax/round_trip_parse_gen.swift +++ b/test/AutoDiff/Syntax/round_trip_parse_gen.swift @@ -37,3 +37,41 @@ func bazDerivative(_ x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { return (x, { v in v }) } + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@derivative(of: A.B.C.foo(label:_:), wrt: x) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@derivative(of: A.B.C.foo(label:_:), wrt: 0) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a6a06dfea4e24..eae2c56ee1ce0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -75,7 +75,8 @@ function(get_test_dependencies SDK result_var_name) ("${SDK}" STREQUAL "FREEBSD") OR ("${SDK}" STREQUAL "ANDROID") OR ("${SDK}" STREQUAL "WINDOWS") OR - ("${SDK}" STREQUAL "HAIKU")) + ("${SDK}" STREQUAL "HAIKU") OR + ("${SDK}" STREQUAL "WASI")) # No extra dependencies. else() message(FATAL_ERROR "Unknown SDK: ${SDK}") @@ -206,34 +207,39 @@ foreach(SDK ${SWIFT_SDKS}) # NOTE create a stub BlocksRuntime library that can be used for the # reflection tests - file(WRITE ${test_bin_dir}/Inputs/BlocksRuntime.c + if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin AND (SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT)) + file(WRITE ${test_bin_dir}/Inputs/BlocksRuntime.c "void #if defined(_WIN32) __declspec(dllexport) #endif _Block_release(void) { }\n") - _add_swift_library_single( - BlocksRuntimeStub${VARIANT_SUFFIX} - BlocksRuntimeStub - SHARED - DONT_EMBED_BITCODE - NOSWIFTRT - ARCHITECTURE ${ARCH} - SDK ${SDK} - INSTALL_IN_COMPONENT dev - ${test_bin_dir}/Inputs/BlocksRuntime.c) - set_target_properties(BlocksRuntimeStub${VARIANT_SUFFIX} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${test_bin_dir} - LIBRARY_OUTPUT_DIRECTORY ${test_bin_dir} - RUNTIME_OUTPUT_DIRECTORY ${test_bin_dir} - OUTPUT_NAME BlocksRuntime) - list(APPEND test_dependencies BlocksRuntimeStub${VARIANT_SUFFIX}) + _add_swift_library_single( + BlocksRuntimeStub${VARIANT_SUFFIX} + BlocksRuntimeStub + SHARED + DONT_EMBED_BITCODE + NOSWIFTRT + ARCHITECTURE ${ARCH} + SDK ${SDK} + INSTALL_IN_COMPONENT dev + ${test_bin_dir}/Inputs/BlocksRuntime.c) + set_target_properties(BlocksRuntimeStub${VARIANT_SUFFIX} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${test_bin_dir} + LIBRARY_OUTPUT_DIRECTORY ${test_bin_dir} + RUNTIME_OUTPUT_DIRECTORY ${test_bin_dir} + OUTPUT_NAME BlocksRuntime) + list(APPEND test_dependencies BlocksRuntimeStub${VARIANT_SUFFIX}) + endif() if(SWIFT_BUILD_STDLIB AND SWIFT_INCLUDE_TESTS) list(APPEND test_dependencies "swift-test-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}") - list(APPEND test_dependencies - "swift-reflection-test${VARIANT_SUFFIX}_signed") + # wasm: Avoid to build swift-reflection-test because it uses unsupported linker flags for wasm-ld + if(NOT "${SDK}" STREQUAL "WASI") + list(APPEND test_dependencies + "swift-reflection-test${VARIANT_SUFFIX}_signed") + endif() endif() if(NOT "${COVERAGE_DB}" STREQUAL "") diff --git a/test/ClangImporter/Inputs/objc_direct.h b/test/ClangImporter/Inputs/objc_direct.h new file mode 100644 index 0000000000000..d283e587c45f1 --- /dev/null +++ b/test/ClangImporter/Inputs/objc_direct.h @@ -0,0 +1,12 @@ +@interface Bar +@property (readonly, direct, nonatomic) int directProperty; +- (void)directMethod __attribute__((objc_direct)); ++ (void)directClassMethod __attribute__((objc_direct)); +@end + +__attribute__((objc_direct_members)) +@interface Bar () +@property (readonly, nonatomic) int directProperty2; +- (void)directMethod2; ++ (void)directClassMethod2; +@end diff --git a/test/ClangImporter/attr-swift_name.swift b/test/ClangImporter/attr-swift_name.swift index 7cb5da9580bbe..f0cb32c55e385 100644 --- a/test/ClangImporter/attr-swift_name.swift +++ b/test/ClangImporter/attr-swift_name.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t.mcp) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp -disable-named-lazy-member-loading 2>&1 | %FileCheck %s // REQUIRES: objc_interop diff --git a/test/ClangImporter/availability_returns_twice.swift b/test/ClangImporter/availability_returns_twice.swift index ef9ff6ffad251..c91e922fb0e89 100644 --- a/test/ClangImporter/availability_returns_twice.swift +++ b/test/ClangImporter/availability_returns_twice.swift @@ -1,5 +1,6 @@ // RUN: %target-typecheck-verify-swift // UNSUPPORTED: OS=windows-msvc +// UNSUPPORTED: OS=wasi // In Android jmp_buf is int[16], which doesn't convert to &Int (SR-9136) // XFAIL: OS=linux-androideabi // XFAIL: OS=linux-android diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift new file mode 100644 index 0000000000000..52d7dae1dd75a --- /dev/null +++ b/test/ClangImporter/clang-function-types.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +import ctypes + +// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +public let f1 = getFunctionPointer_() + +// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +public let f2 = getHigherOrderFunctionPointer() + +// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +public let f3 = getFunctionPointer3 diff --git a/test/ClangImporter/disable-source-import.swift b/test/ClangImporter/disable-source-import.swift new file mode 100644 index 0000000000000..826a2b10d2e24 --- /dev/null +++ b/test/ClangImporter/disable-source-import.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t.mcp) + +// This should fail only if -disable-clangimporter-source-import is present. + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp %s | %FileCheck --allow-empty %s + +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -o - -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp -disable-clangimporter-source-import %s \ +// RUN: 2>&1 | %FileCheck --check-prefix=ERROR %s + +import ExternIntX + +public let y = x // ERROR: error + +// CHECK-NOT: error diff --git a/test/ClangImporter/objc_direct.swift b/test/ClangImporter/objc_direct.swift new file mode 100644 index 0000000000000..494e002b8d46e --- /dev/null +++ b/test/ClangImporter/objc_direct.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_direct.h -verify-ignore-unknown + +// REQUIRES: objc_interop + +func callThingsOnBar(_ x: Bar) { + let _ = x.directProperty // expected-error {{getter for 'directProperty' is unavailable in Swift}} + let _ = x.directProperty2 // expected-error {{getter for 'directProperty2' is unavailable in Swift}} + + x.directMethod() // expected-error {{'directMethod()' is unavailable in Swift}} + x.directMethod2() // expected-error {{'directMethod2()' is unavailable in Swift}} + + Bar.directClassMethod() // expected-error {{'directClassMethod()' is unavailable in Swift}} + Bar.directClassMethod2() // expected-error {{'directClassMethod2()' is unavailable in Swift}} +} diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift index 58817ab06c170..01087fb0038e8 100644 --- a/test/ClangImporter/objc_parse.swift +++ b/test/ClangImporter/objc_parse.swift @@ -550,8 +550,8 @@ func testProtocolQualified(_ obj: CopyableNSObject, cell: CopyableSomeCell, _ = cell as NSCopying _ = cell as SomeCell - _ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}} - _ = plainCell as CopyableSomeCell // expected-error {{'SomeCell' is not convertible to 'CopyableSomeCell' (aka 'SomeCell & NSCopying'); did you mean to use 'as!' to force downcast?}} + _ = plainObj as CopyableNSObject // expected-error {{value of type 'NSObject' does not conform to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') in coercion}} + _ = plainCell as CopyableSomeCell // expected-error {{value of type 'SomeCell' does not conform to 'CopyableSomeCell' (aka 'SomeCell & NSCopying') in coercion}} } extension Printing { diff --git a/test/Constraints/ErrorBridging.swift b/test/Constraints/ErrorBridging.swift index d80afc5d06d4e..81a8a7675fc60 100644 --- a/test/Constraints/ErrorBridging.swift +++ b/test/Constraints/ErrorBridging.swift @@ -73,3 +73,24 @@ extension Error { func throwErrorCode() throws { throw FictionalServerError.meltedDown // expected-error{{thrown error code type 'FictionalServerError.Code' does not conform to 'Error'; construct an 'FictionalServerError' instance}}{{29-29=(}}{{40-40=)}} } + +class MyErrorClass { } +extension MyErrorClass: Error { } + +class MyClass { } + +func testUnknownErrorBridge(cond: Bool, mc: MyClass) -> NSObject? { + if cond { + return mc as? NSError // okay + } + + return mc as? NSObject // okay +} + +func testAlwaysErrorBridge(cond: Bool, mec: MyErrorClass) -> NSObject? { + if cond { + return mec as? NSError // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSError' always succeeds + } + + return mec as? NSObject // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSObject' always succeeds +} diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index b1e02cf5ff935..3747e66dec5c5 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -222,3 +222,19 @@ func compare(_: T, _: T) {} // expected-note {{'compare' declared here}} func compare(_: T?, _: T?) {} _ = nil? as? Int?? // expected-error {{nil literal cannot be the source of a conditional cast}} + +func test_tuple_casts_no_warn() { + struct Foo {} + + let arr: [(Any, Any)] = [(Foo(), Foo())] + let tup: (Any, Any) = (Foo(), Foo()) + + _ = arr as! [(Foo, Foo)] // Ok + _ = tup as! (Foo, Foo) // Ok + + _ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}} + _ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}} + + _ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}} + _ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}} +} diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 59a990f33a8d1..869096937ff8d 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -249,10 +249,10 @@ var _: (Int,Int) -> Int = {$0+$1+$2} // expected-error {{contextual closure typ // Crash when re-typechecking bodies of non-single expression closures struct CC {} -func callCC(_ f: (CC) -> U) -> () {} // expected-note {{in call to function 'callCC'}} +func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{generic parameter 'U' could not be inferred}} + callCC { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} _ = $0 return 1 } @@ -313,7 +313,7 @@ struct Thing { init?() {} } // This throws a compiler error -let things = Thing().map { thing in // expected-error {{generic parameter 'U' could not be inferred}} +let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} // Commenting out this makes it compile _ = thing return thing @@ -322,7 +322,7 @@ let things = Thing().map { thing in // expected-error {{generic parameter 'U' c // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> String in }} + let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} if true { return "foo" } @@ -360,7 +360,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{generic parameter 'T' could not be inferred}} +[0].map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} _ in let r = (1,2).0 return r @@ -408,7 +408,7 @@ func r20789423() { print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> String }} + let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} print("a") return "hi" } @@ -443,7 +443,7 @@ class C_SR_2505 : P_SR_2505 { func call(_ c: C_SR_2505) -> Bool { // Note: the diagnostic about capturing 'self', indicates that we have // selected test(_) rather than test(it:) - return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit 'self.' to make capture semantics explicit}} + return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} expected-note{{reference 'self.' explicitly}} } } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6c98ae232b116..00e86417b19ef 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -146,7 +146,7 @@ func ***~(_: Int, _: String) { } i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} @available(*, unavailable, message: "call the 'map()' method on the sequence") -public func myMap( // expected-note {{in call to function 'myMap'}} +public func myMap( _ source: C, _ transform: (C.Iterator.Element) -> T ) -> [T] { fatalError("unavailable function can't be called") @@ -159,7 +159,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{generic parameter 'T' could not be inferred}} + myMap(0..<10, { x in // expected-error{{unable to infer complex closure return type; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} () return x }) @@ -251,7 +251,8 @@ struct Toe { let toenail: Nail // expected-error {{use of undeclared type 'Nail'}} func clip() { - toenail.inspect { x in + // FIXME: We shouldn't report this because toenail.inspect is a hole + toenail.inspect { x in // expected-error {{unable to infer closure return type; add explicit type to disambiguate}} toenail.inspect { y in } } } @@ -288,7 +289,8 @@ func r18800223(_ i : Int) { var buttonTextColor: String? - _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{unable to infer closure type in the current context}} + _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{result values in '? :' expression have mismatching types 'Int' and '(_) -> _'}} + // expected-error@-1 {{unable to infer closure return type; add explicit type to disambiguate}} } // Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back @@ -492,7 +494,9 @@ enum Color { static func rainbow() -> Color {} static func overload(a : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(a:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (a: Int)}} static func overload(b : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(b:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (b: Int)}} static func frob(_ a : Int, b : inout Int) -> Color {} static var svar: Color { return .Red } @@ -517,8 +521,7 @@ let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of t let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}} let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -let _: Color = .overload(1.0) // expected-error {{ambiguous reference to member 'overload'}} -// expected-note @-1 {{overloads for 'overload' exist with these partially matching parameter lists: (a: Int), (b: Int)}} +let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .overload(1) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .frob(1.0, &i) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} @@ -529,8 +532,8 @@ let _: Color = .frob(1, b: i) // expected-error {{passing value of type 'Int' t let _: Color = .frob(1, &d) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} let _: Color = .frob(1, b: &d) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} -someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} +var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} +someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} someColor = .svar() // expected-error {{cannot call value of non-function type 'Color'}} someColor = .svar(1) // expected-error {{cannot call value of non-function type 'Color'}} diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index 2b162eb03d9db..5a918dacd6fa1 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -121,7 +121,7 @@ func rdar34583132() { func bar(_ s: S) { guard s.foo(1 + 2) == .timeout else { - // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'}} + // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'?}} fatalError() } } diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index 9d66e0123796a..08930eff733d6 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -234,3 +234,16 @@ tuplify(true) { x in #warning("oops") // expected-warning{{oops}} 3.14159 } + +struct MyTuplifiedStruct { + var condition: Bool + + @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the function builder}}{{3-17=}} + if condition { + return 17 // expected-warning{{application of function builder 'TupleBuilder' disabled by explicit 'return' statement}} + // expected-note@-1{{remove 'return' statements to apply the function builder}}{{7-14=}}{{12-19=}} + } else { + return 42 + } + } +} diff --git a/test/Constraints/generic_protocol_witness.swift b/test/Constraints/generic_protocol_witness.swift index 75f10061cb8c7..82d9da908aad0 100644 --- a/test/Constraints/generic_protocol_witness.swift +++ b/test/Constraints/generic_protocol_witness.swift @@ -59,5 +59,6 @@ func usesAGenericMethod(_ x: U) { struct L: Sequence {} // expected-error {{type 'L' does not conform to protocol 'Sequence'}} func z(_ x: L) { - for xx in x {} // expected-warning {{immutable value 'xx' was never used; consider replacing with '_' or removing it}} + for xx in x {} + // expected-warning@-1{{immutable value 'xx' was never used; consider replacing with '_' or removing it}} } diff --git a/test/Constraints/iuo_objc.swift b/test/Constraints/iuo_objc.swift index 1d11b98423eca..22c5ae63f479e 100644 --- a/test/Constraints/iuo_objc.swift +++ b/test/Constraints/iuo_objc.swift @@ -9,7 +9,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional!() let _: Coat? = prop.iuo.optional!()! let _: Coat? = prop.iuo!.optional() @@ -17,7 +19,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional!() let _: Coat? = prop.iuo!.optional!()! let _: Coat = prop.iuo.optional() @@ -25,7 +29,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional!() let _: Coat = prop.iuo.optional!()! let _: Coat = prop.iuo!.optional() @@ -34,7 +40,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional!() let _: Coat = prop.iuo!.optional!()! diff --git a/test/Constraints/keypath_dynamic_member_lookup.swift b/test/Constraints/keypath_dynamic_member_lookup.swift index 021434bd58488..68b7eac7f0c07 100644 --- a/test/Constraints/keypath_dynamic_member_lookup.swift +++ b/test/Constraints/keypath_dynamic_member_lookup.swift @@ -439,7 +439,6 @@ struct SR_11896_Immutable { } } - // CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup21testKeyPathMutabilityyyF : $@convention(thin) () -> () func testKeyPathMutability() { // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int) @@ -458,3 +457,29 @@ func testKeyPathMutability() { // CHECK: keypath $KeyPath, (root $SR_11896_Immutable; gettable_property $Int _ = \SR_11896_Immutable.immutable } + +// SR-11933: Make sure we properly handle default arguments. +struct HasDefaultedSubscript { + subscript(_ x: Int = 0) -> Int { x } +} + +@dynamicMemberLookup +struct SR_11933 { + subscript(dynamicMember kp: KeyPath) -> Int { 0 } +} + +// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup28testDynamicMemberWithDefaultyyAA8SR_11933VF : $@convention(thin) (SR_11933) -> () +func testDynamicMemberWithDefault(_ x: SR_11933) { + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[SUB_GET:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int + // CHECK: apply [[SUB_GET]]([[KP]], {{%[0-9]+}}) + _ = x[] + + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[OUTER_KP:%[0-9]+]] = keypath $KeyPath, (root $SR_11933; gettable_property $Int, id @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int, getter @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11933, UnsafeRawPointer) -> @out Int, indices [%$0 : $KeyPath : $KeyPath], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]]) + _ = \SR_11933.[] +} diff --git a/test/Constraints/keyword_arguments.swift b/test/Constraints/keyword_arguments.swift index 977943e116529..6458fd4c36a66 100644 --- a/test/Constraints/keyword_arguments.swift +++ b/test/Constraints/keyword_arguments.swift @@ -349,14 +349,14 @@ func trailingClosure6(value: Int, expression: () -> T?) { } trailingClosure5(file: "hello", line: 17) { // expected-error{{extraneous argument label 'file:' in call}}{{18-24=}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} return Optional.Some(5) - // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{19-23=some}} + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} } trailingClosure6(5) { // expected-error{{missing argument label 'value:' in call}}{{18-18=value: }} // expected-error@-1 {{generic parameter 'T' could not be inferred}} return Optional.Some(5) - // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{19-23=some}} + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} } diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 479179e50591d..7a39dd010c393 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -616,8 +616,11 @@ func rdar50679161() { func rdar_50467583_and_50909555() { // rdar://problem/50467583 - let _: Set = [Int][] - // expected-error@-1 {{instance member 'subscript' cannot be used on type '[Int]'}} + let _: Set = [Int][] // expected-error {{no exact matches in call to subscript}} + // expected-note@-1 {{found candidate with type '(Int) -> Int'}} + // expected-note@-2 {{found candidate with type '(Range) -> ArraySlice'}} + // expected-note@-3 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} + // expected-note@-4 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} // rdar://problem/50909555 struct S { diff --git a/test/Constraints/one_way_constraints.swift b/test/Constraints/one_way_constraints.swift index a56c143246f40..d830367d35d78 100644 --- a/test/Constraints/one_way_constraints.swift +++ b/test/Constraints/one_way_constraints.swift @@ -10,11 +10,11 @@ func testTernaryOneWay(b: Bool) { let _: Float = b ? 3.14159 : 17 // Errors due to one-way inference. - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{result values in '? :' expression have mismatching types 'Double' and 'Int'}} : 17 - let _: Float = b ? 3.14159 - : Builtin.one_way(17) // expected-error{{cannot convert value of type 'Int' to specified type 'Float'}} - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? 3.14159 // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} + : Builtin.one_way(17) + let _: Float = b ? Builtin.one_way(3.14159) // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} : Builtin.one_way(17) // Okay: default still works. diff --git a/test/Constraints/operator.swift b/test/Constraints/operator.swift index 14cdeb624d2e2..3b85bda0b8792 100644 --- a/test/Constraints/operator.swift +++ b/test/Constraints/operator.swift @@ -218,7 +218,6 @@ func rdar46459603() { _ = arr.values == [e] // expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary.Values' and '[E]'}} - // expected-note@-2 {{expected an argument list of type '(Self, Self)'}} _ = [arr.values] == [[e]] // expected-error@-1 {{value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}} // expected-note@-2 {{requirement from conditional conformance of '[Any]' to 'Equatable'}} diff --git a/test/Constraints/overload.swift b/test/Constraints/overload.swift index da7dae970f8f5..5838918cba3be 100644 --- a/test/Constraints/overload.swift +++ b/test/Constraints/overload.swift @@ -127,12 +127,11 @@ func test20886179(_ handlers: [(Int) -> Void], buttonIndex: Int) { // The problem here is that the call has a contextual result type incompatible // with *all* overload set candidates. This is not an ambiguity. -func overloaded_identity(_ a : Int) -> Int {} -func overloaded_identity(_ b : Float) -> Float {} +func overloaded_identity(_ a : Int) -> Int {} // expected-note {{found this candidate}} +func overloaded_identity(_ b : Float) -> Float {} // expected-note {{found this candidate}} func test_contextual_result_1() { - return overloaded_identity() // expected-error {{cannot invoke 'overloaded_identity' with no arguments}} - // expected-note @-1 {{overloads for 'overloaded_identity' exist with these partially matching parameter lists: (Float), (Int)}} + return overloaded_identity() // expected-error {{no exact matches in call to global function 'overloaded_identity'}} } func test_contextual_result_2() { diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index d391c73e48f0e..ef03365f1ce4a 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -211,7 +211,6 @@ struct A : PP { extension PP { func map(_ f: (Self.E) -> T) -> T {} - // expected-note@-1 2 {{in call to function 'map'}} } enum EE { @@ -231,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{generic parameter 'T' could not be inferred}} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{generic parameter 'T' could not be inferred}} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} switch $0 { case .A: return 1 diff --git a/test/Constraints/rdar46377919.swift b/test/Constraints/rdar46377919.swift index ffdaab5b384e9..2f95e1cf28ec6 100644 --- a/test/Constraints/rdar46377919.swift +++ b/test/Constraints/rdar46377919.swift @@ -9,5 +9,4 @@ class Foo { func foo() -> Foo { return Foo(lhs: 2, rhs: 2) - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '<>'}} } diff --git a/test/Constraints/sr4664.swift b/test/Constraints/sr4664.swift new file mode 100644 index 0000000000000..d3a10fd8f918a --- /dev/null +++ b/test/Constraints/sr4664.swift @@ -0,0 +1,22 @@ +// RUN: %target-typecheck-verify-swift + +struct M where T : Collection { // expected-note {{where 'T' = 'X.Y'}} + static func f(a: T, b: T) -> [E] { + } +} + +enum E {} + +struct S {} + +struct X { + struct Y { + let s: [S] + } + + let y: [Y] +} + +let x = X(y: []) +let a = M.f(a: x.y[0], b: x.y[1]) +// expected-error@-1 {{generic struct 'M' requires that 'X.Y' conform to 'Collection'}} diff --git a/test/Constraints/super_constructor.swift b/test/Constraints/super_constructor.swift index c87cfead80eba..01e15a2990432 100644 --- a/test/Constraints/super_constructor.swift +++ b/test/Constraints/super_constructor.swift @@ -20,8 +20,7 @@ class D : B { } init(g:Int) { - super.init("aoeu") // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'B.init' exist with these partially matching parameter lists: (a: UnicodeScalar), (b: UnicodeScalar), (x: Int), (z: Float)}} + super.init("aoeu") // expected-error{{no exact matches in call to initializer}} } init(h:Int) { @@ -40,15 +39,15 @@ class B { init() { } - init(x:Int) { + init(x:Int) { // expected-note{{candidate has partially matching parameter list (x: Int)}} } - init(a:UnicodeScalar) { + init(a:UnicodeScalar) { // expected-note {{candidate has partially matching parameter list (a: UnicodeScalar)}} } - init(b:UnicodeScalar) { + init(b:UnicodeScalar) { // expected-note{{candidate has partially matching parameter list (b: UnicodeScalar)}} } - init(z:Float) { + init(z:Float) { // expected-note{{candidate has partially matching parameter list (z: Float)}} super.init() // expected-error{{'super' members cannot be referenced in a root class}} } } diff --git a/test/DebugInfo/liverange-extension-vector.swift b/test/DebugInfo/liverange-extension-vector.swift deleted file mode 100644 index 7320a1c4a4aa7..0000000000000 --- a/test/DebugInfo/liverange-extension-vector.swift +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s -// REQUIRES: objc_interop, CPU=x86_64 -import simd - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -public func rangeExtension(x: Int32, y: Int32) { - let p = int2(x, y) - // CHECK: define {{.*}}rangeExtension - // CHECK: llvm.dbg.value(metadata <2 x i32> %[[P:.*]], metadata {{.*}}, metadata - use(p) - // CHECK: asm sideeffect "", "r"{{.*}}[[P]] -} diff --git a/test/DebugInfo/liverange-extension.swift b/test/DebugInfo/liverange-extension.swift deleted file mode 100644 index a7ba868005393..0000000000000 --- a/test/DebugInfo/liverange-extension.swift +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s - -// REQUIRES: CPU=x86_64 -// -// We require x86_64 to make the test easier to read. Without this, -// writing check lines that ensure our asm gadgets match up with the -// right values is painfully hard to do. - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -// CHECK-LABEL: define {{.*}}rangeExtension -public func rangeExtension(_ b: Bool) { - let i = getInt32() - // CHECK: [[I:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I:!.*]], metadata - - use(i) - - // CHECK: br i1 - - if b { - // CHECK: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I]] - - let j = getInt32() - // CHECK: [[J:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - // CHECK: llvm.dbg.value(metadata i32 [[J]], metadata [[MD_J:!.*]], metadata - - use(j) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - - // CHECK: br label - } - - // CHECK-NOT: llvm.dbg.value(metadata i32 [[J]] - // CHECK: llvm.dbg.value(metadata i32 [[I]] - - let z = getInt32() - // CHECK: [[Z:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK: llvm.dbg.value(metadata i32 [[Z]], metadata [[MD_Z:!.*]], metadata - - use(z) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[Z_ZEXT:%.*]] = zext i32 [[Z]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[Z_ZEXT]]) -} - -// CHECK-DAG: [[MD_I]] = !DILocalVariable(name: "i" -// CHECK-DAG: [[MD_J]] = !DILocalVariable(name: "j" -// CHECK-DAG: [[MD_Z]] = !DILocalVariable(name: "z" diff --git a/test/DebugInfo/shadow_copies.swift b/test/DebugInfo/shadow_copies.swift index 7d348c8c5849f..69a04f7bd9d84 100644 --- a/test/DebugInfo/shadow_copies.swift +++ b/test/DebugInfo/shadow_copies.swift @@ -18,14 +18,14 @@ class ClassB : ClassA override init (_ input : Int64) { // CHECK: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" - // NOPCOPY: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" + // NOCOPY: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" // CHECK: alloca {{.*}}ClassBC* - // NOPCOPY: alloca {{.*}}ClassBC* + // NOCOPY: alloca {{.*}}ClassBC* // CHECK: alloca i64 // CHECK-NOT: alloca - // NOPCOPY-NOT: alloca + // NOCOPY-NOT: alloca // CHECK: ret {{.*}}ClassBC // NOCOPY: ret {{.*}}ClassBC super.init (input) @@ -33,3 +33,30 @@ class ClassB : ClassA } let b = ClassB(1); + +func use(_ x: Int) {} + +class ClassC +{ + // CHECK: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + // NOCOPY: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + init () + { + // CHECK: alloca %T13shadow_copies6ClassCC* + // CHECK-NOT: alloca + // NOCOPY-NOT: alloca + + // CHECK: call void @llvm.dbg.value(metadata i64 10 + // NOCOPY: call void @llvm.dbg.value(metadata i64 10 + let x = 10 + + use(x) + + use(x) + + // CHECK: ret + // NOCOPY: ret + } +} + +let c = ClassC() diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index 4a32e9a8f0aa2..0e1216db48a36 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -1,27 +1,94 @@ -// RUN: echo "{\"%/s\": {\"object\": \"/build/obj/advanced_output_file_map.o\", \"swiftmodule\": \"/build/swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"/build/dia/advanced_output_file_map.dia\", \"dependencies\": \"/build/d/advanced_output_file_map.d\"}, \"%/S/Inputs/main.swift\": {\"object\": \"/build/obj/main.o\", \"swiftmodule\": \"/build/swiftmodule/main.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/main_x.swiftdoc", \"diagnostics\": \"/build/dia/main.dia\", \"dependencies\": \"/build/d/main.d\"}, \"%/S/Inputs/lib.swift\": {\"object\": \"/build/obj/lib.o\", \"swiftmodule\": \"/build/swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"/build/swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"/build/dia/lib.dia\", \"dependencies\": \"/build/d/lib.d\"}}" > %t.json - -// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM -// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS - -// DUMPOFM: {{.*}}/Inputs/lib.swift -> object: "/build/obj/lib.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "/build/d/lib.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "/build/swiftmodule/lib.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "/build/swiftmodule/lib_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "/build/dia/lib.dia" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> object: "/build/obj/main.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "/build/d/main.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "/build/swiftmodule/main.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "/build/swiftmodule/main_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "/build/dia/main.dia" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "/build/obj/advanced_output_file_map.o" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "/build/d/advanced_output_file_map.d" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "/build/dia/advanced_output_file_map.dia" - -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "/build/obj/advanced_output_file_map.o", dependencies: "/build/d/advanced_output_file_map.d", swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "/build/dia/advanced_output_file_map.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "/build/obj/main.o", dependencies: "/build/d/main.d", swiftmodule: "/build/swiftmodule/main.swiftmodule", swiftdoc: "/build/swiftmodule/main_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "/build/dia/main.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "/build/obj/lib.o", dependencies: "/build/d/lib.d", swiftmodule: "/build/swiftmodule/lib.swiftmodule", swiftdoc: "/build/swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "/build/dia/lib.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o"], output: {swiftmodule: "/build/OutputFileMap.swiftmodule", swiftdoc: "/build{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: "/build{{[/\\]}}OutputFileMap.swiftsourceinfo"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o", "/build/OutputFileMap.swiftmodule"], output: {image: "/build/advanced_output_file_map.out"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["/build/advanced_output_file_map.out"], output: {dSYM: "/build/advanced_output_file_map.out.dSYM"} +// Test both ways: -disable-only-one-dependency-file, and the default which should be same as -enable-only-one-dependency-file + +// RUN: %empty-directory(%t) + +// Create an output file map +// RUN: echo "{\"%/s\": {\"object\": \"./obj/advanced_output_file_map.o\", \"swiftmodule\": \"./swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "./swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"./dia/advanced_output_file_map.dia\", \"dependencies\": \"./d/advanced_output_file_map.d\"}, " >%t/ofm.json +// RUN: echo " \"%/S/Inputs/main.swift\": {\"object\": \"./obj/main.o\", \"swiftmodule\": \"./swiftmodule/main.swiftmodule\", \"swiftdoc\": \"./swiftmodule/main_x.swiftdoc\", \"diagnostics\": \"./dia/main.dia\", \"dependencies\": \"./d/main.d\"}, " >> %t/ofm.json +// RUN: echo " \"%/S/Inputs/lib.swift\": {\"object\": \"./obj/lib.o\", \"swiftmodule\": \"./swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"./swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"./dia/lib.dia\", \"dependencies\": \"./d/lib.d\"}}" >> %t/ofm.json + +// With -disable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS + +// DUMPOFM-DIS: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS + +// Should be no dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test ! -e %t/d/main.d +// RUN: test ! -e %t/d/lib.d + + +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", dependencies: "./d/main.d", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", dependencies: "./d/lib.d", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + + +// With -enable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + + +// DUMPOFM-ENA: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-ENA + + +// Should be two dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d + + +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + +// Defaulting to: -enable-only-one-dependency-file + +// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + + +// RUN: %empty-directory(%t/d) +// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | tee /tmp/out | %FileCheck %/s -check-prefix=BINDINGS-ENA +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d diff --git a/test/Driver/ast_dump_with_WMO.swift b/test/Driver/ast_dump_with_WMO.swift new file mode 100644 index 0000000000000..2b2a142f730c8 --- /dev/null +++ b/test/Driver/ast_dump_with_WMO.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) + + +// Check that -wmo is ignored when -dump-ast is present and that the AST gets +// dumped to stdout, no matter the order of the options + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_WMO_dump | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %swiftc_driver -dump-ast -whole-module-optimization -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_dump_WMO | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_dump +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_dump_WMO + + +// Check that ignoring -wmo doesn't affect the output file paths for the AST +// dumps + +// RUN: cd %t +// RUN: echo ' ' > a.swift +// RUN: echo ' ' > main.swift +// RUN: echo '{ "a.swift": { "ast-dump": "a.ast" }, "main.swift": { "ast-dump": "main.ast" }}' > outputFileMap.json + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main -output-file-map=outputFileMap.json main.swift a.swift -### 2>%t/stderr_WMO_OFM | %FileCheck -check-prefix CHECK-OFM %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_OFM + + + +// CHECK-WMO: warning: ignoring '-wmo' because '-dump-ast' was also specified + +// CHECK-STDOUT-NOT: -whole-module-optimization +// CHECK-STDOUT-NOT: -wmo +// CHECK-STDOUT: -dump-ast +// CHECK-STDOUT: -o - + +// CHECK-OFM-NOT: -whole-module-optimization +// CHECK-OFM-NOT: -wmo +// CHECK-OFM: -dump-ast +// CHECK-OFM-SAME: -o main.ast +// CHECK-OFM-NEXT: -dump-ast +// CHECK-OFM-SAME: -o a.ast diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift new file mode 100644 index 0000000000000..5fb992c6d8f62 --- /dev/null +++ b/test/Driver/lock_interface.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func foo() {}' > %t/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution +// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift +// RUN: echo 'import Foo' > %t/file-01.swift +// RUN: echo 'import Foo' > %t/file-02.swift +// RUN: echo 'import Foo' > %t/file-03.swift +// RUN: %target-swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt +// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt + +// Ensure we only build Foo module once from the interface +// CHECK-REBUILD: rebuilding module 'Foo' from interface +// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface diff --git a/test/Driver/print_target_info.swift b/test/Driver/print_target_info.swift index cd2bd5c7460cd..28a65dea78d06 100644 --- a/test/Driver/print_target_info.swift +++ b/test/Driver/print_target_info.swift @@ -6,6 +6,7 @@ // CHECK-IOS: "target": { // CHECK-IOS: "triple": "arm64-apple-ios12.0", +// CHECK-IOS: "unversionedTriple": "arm64-apple-ios", // CHECK-IOS: "moduleTriple": "arm64-apple-ios", // CHECK-IOS: "swiftRuntimeCompatibilityVersion": "5.0", // CHECK-IOS: "librariesRequireRPath": true diff --git a/test/Driver/print_target_info_macos.swift b/test/Driver/print_target_info_macos.swift index 4121ab3810a4a..14074424cf484 100644 --- a/test/Driver/print_target_info_macos.swift +++ b/test/Driver/print_target_info_macos.swift @@ -7,4 +7,5 @@ // REQUIRES: OS=macosx -// CHECK: "triple": "x86_64-apple-macosx +// CHECK: "triple": "x86_64-apple-macosx10 +// CHECK: "unversionedTriple": "x86_64-apple-macosx" diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index e60cef430ce6f..9aa0ab436477c 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -53,7 +53,7 @@ func otherExistential(_ t1: T) { otherEqComp2 = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp2 - _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{'T' is not convertible to 'EqualComparable & OtherEqualComparable'; did you mean to use 'as!' to force downcast?}} {{10-12=as!}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} + _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} } protocol Runcible { diff --git a/test/IDE/comment_brief.swift b/test/IDE/comment_brief.swift index 1409f3e9fd36f..db447aaca1807 100644 --- a/test/IDE/comment_brief.swift +++ b/test/IDE/comment_brief.swift @@ -145,8 +145,8 @@ struct Indentation { // CHECK-NEXT: Func/briefBlockWithASCIIArt5 {{.*}} BriefComment=[Aaa. Bbb. *Ccc.] // CHECK-NEXT: Func/briefBlockWithASCIIArt6 {{.*}} BriefComment=[Aaa.] // CHECK-NEXT: Func/briefMixed1 {{.*}} BriefComment=[Aaa. Bbb.] -// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa.] -// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa.] +// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa. Bbb.] +// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa. Bbb.] // CHECK-NEXT: Struct/Indentation RawComment=none // CHECK-NEXT: Func/Indentation.briefBlockWithASCIIArt1 {{.*}} BriefComment=[Aaa.] diff --git a/test/IDE/complete_after_self.swift b/test/IDE/complete_after_self.swift index 913f8ecf2f851..0f616400ab595 100644 --- a/test/IDE/complete_after_self.swift +++ b/test/IDE/complete_after_self.swift @@ -240,9 +240,9 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .staticTest2()[#Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: .DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/Super: .baseFunc0({#(self): ThisBase1#})[#() -> Void#] @@ -275,9 +275,9 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: staticTest2()[#Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/Super: baseFunc0({#(self): ThisBase1#})[#() -> Void#] diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 80c5c93b13556..50c58a4dbef6b 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -128,16 +128,16 @@ func f2() { } // ASSIGN_5: Begin completions, 2 items -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#C1.D1#]; name=case2 -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#C1.D1#]; name=case1 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#D1#]; name=case2 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#D1#]; name=case1 func f6() { var d : D2 d = .#^ASSIGN_6^# } // ASSIGN_6: Begin completions, 2 items -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#C1.D2#]; name=case3 -// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#C1.D2#]; name=case4 +// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#D2#]; name=case3 +// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#D2#]; name=case4 func f7 (C : C2) { var i : Int @@ -147,10 +147,10 @@ func f2() { // ASSIGN_7: Begin completions // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f8 (C : C2) { var i : Int? @@ -160,10 +160,10 @@ func f2() { // ASSIGN_8: Begin completions // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: IntGen()[#Int#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntOpGen()[#Int?#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f9 (C : C2) { var d : D1 @@ -173,10 +173,10 @@ func f2() { // ASSIGN_9: Begin completions // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f10 (C : C2) { var d : D1 @@ -186,10 +186,10 @@ func f2() { // ASSIGN_10: Begin completions // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f11(C: C2) { d = C.#^ASSIGN_11^# @@ -198,10 +198,10 @@ func f2() { // ASSIGN_11: Begin completions // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: VoidGen()[#Void#] -// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f12() { var i : Int @@ -211,10 +211,10 @@ func f2() { // ASSIGN_12: Begin completions // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f13() { var i : Int = #^ASSIGN_13^# @@ -250,10 +250,10 @@ func f2() { // ASSIGN_16: Begin completions // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f17() { var i : Int = C2Gen().#^ASSIGN_17^# @@ -262,10 +262,10 @@ func f2() { // ASSIGN_17: Begin completions // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f18 (C : C2) { var d : D1 = C.#^ASSIGN_18^# @@ -274,10 +274,10 @@ func f2() { // ASSIGN_18: Begin completions // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] } diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index 65d4457e21c4c..6d75f7b6afcc9 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -338,7 +338,7 @@ struct ClosureInInit1 { var prop1: S = { return S(#^CLOSURE_IN_INIT_1^# } -// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#ClosureInInit1.S#]; +// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#S#]; var prop2: S = { return S(#^CLOSURE_IN_INIT_2^# }() diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index a3d260645c36b..76d93abec633c 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -69,7 +69,7 @@ struct CustomGenericCollection : ExpressibleByDictionaryLiteral { // GENERIC_PARAM_AND_ASSOC_TYPE: Begin completions // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended/TypeRelation[Identical]: count[#Int#]; name=count // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[GenericTypeParam]/Local: Key[#Key#]; name=Key - // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#CustomGenericCollection.Value#]; name=Value + // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#Value#]; name=Value // GENERIC_PARAM_AND_ASSOC_TYPE: End completions var count: Int { #^GENERIC_PARAM_AND_ASSOC_TYPE^# } diff --git a/test/IDE/complete_crossmodule.swift b/test/IDE/complete_crossmodule.swift new file mode 100644 index 0000000000000..f52c8d238c20b --- /dev/null +++ b/test/IDE/complete_crossmodule.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Test.swift -I %t -code-completion-token=OPAQUE_RESULT | %FileCheck --check-prefix=OPAQUE_RESULT %s + +// BEGIN MyModule.swift + +public protocol HasAssocWithConstraint { + associatedtype AssocWithContraint: HasAssocWithConstraint + var value: AssocWithContraint { get } +} + +// BEGIN Test.swift +import MyModule + +struct MyValue: HasAssocWithConstraint { + var #^OPAQUE_RESULT^# +// OPAQUE_RESULT: Begin completions +// OPAQUE_RESULT-DAG: Decl[InstanceVar]/Super: value: some HasAssocWithConstraint; +// OPAQUE_RESULT: End completions +} diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 192772635f9a9..e6528dbb15489 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -65,6 +65,7 @@ struct MyStruct {} // KEYWORD2-NEXT: Keyword/None: differentiable[#Func Attribute#]; name=differentiable // KEYWORD2-NEXT: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction{{$}} // KEYWORD2-NEXT: Keyword/None: derivative[#Func Attribute#]; name=derivative +// KEYWORD2-NEXT: Keyword/None: transpose[#Func Attribute#]; name=transpose // KEYWORD2-NOT: Keyword // KEYWORD2: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // KEYWORD2: End completions @@ -178,6 +179,7 @@ struct _S { // ON_METHOD-DAG: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction // ON_METHOD-DAG: Keyword/None: differentiable[#Func Attribute#]; name=differentiable // ON_METHOD-DAG: Keyword/None: derivative[#Func Attribute#]; name=derivative +// ON_METHOD-DAG: Keyword/None: transpose[#Func Attribute#]; name=transpose // ON_METHOD-NOT: Keyword // ON_METHOD: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_METHOD: End completions @@ -234,6 +236,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder // ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // ON_MEMBER_LAST-DAG: Keyword/None: derivative[#Declaration Attribute#]; name=derivative +// ON_MEMBER_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose // ON_MEMBER_LAST-NOT: Keyword // ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_MEMBER_LAST-NOT: Decl[PrecedenceGroup] @@ -279,6 +282,7 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} // KEYWORD_LAST-NEXT: Keyword/None: derivative[#Declaration Attribute#]; name=derivative +// KEYWORD_LAST-NEXT: Keyword/None: transpose[#Declaration Attribute#]; name=transpose // KEYWORD_LAST-NOT: Keyword // KEYWORD_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // KEYWORD_LAST: End completions diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index 71698cc0af650..3acd80ee120dc 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -66,6 +66,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_STRING_1 | %FileCheck %s -check-prefix=INFIX_STRING // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_EXT_STRING_1 | %FileCheck %s -check-prefix=INFIX_EXT_STRING +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONFORM_SEQUENCE | %FileCheck %s -check-prefix=CONFORM_SEQUENCE + // NO_STDLIB_PRIVATE: Begin completions // NO_STDLIB_PRIVATE: End completions @@ -286,3 +288,14 @@ func testInfixOperator4(_ x: String) { // INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: && {#Bool#}[#Bool#] // INFIX_EXT_STRING-NOT: == // INFIX_EXT_STRING: End completions + +class TestSequence : Sequence { +#^CONFORM_SEQUENCE^# +// CONFORM_SEQUENCE: Begin completions +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Element = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Iterator = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func makeIterator() -> some IteratorProtocol {|}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceVar]/Super: var underestimatedCount: Int; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? {|}; +// CONFORM_SEQUENCE: End completions +} diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_function_builder.swift index 8f97cf8e05414..480a63359c530 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_function_builder.swift @@ -51,14 +51,14 @@ let globalStringVal: String = "" func testAcceptColorTagged(paramIntVal: Int, paramStringVal: String) { let taggedValue = paramIntVal.tag(Color.red) - + acceptColorTagged { color in #^IN_CLOSURE_TOP^# // IN_CLOSURE_TOP_CONTEXT: Begin completions -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: taggedValue[#Tagged#]; name=taggedValue +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local{{.*}}: taggedValue[#Tagged#]; name=taggedValue // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalIntVal[#Int#]; name=globalIntVal // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalStringVal[#String#]; name=globalStringVal -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color; name=color +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color{{.*}}; name=color // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramIntVal[#Int#]; name=paramIntVal // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramStringVal[#String#]; name=paramStringVal // IN_CLOSURE_TOP: End completions diff --git a/test/IDE/complete_member_decls_from_parent_decl_context.swift b/test/IDE/complete_member_decls_from_parent_decl_context.swift index ca15843815cdb..55a28528f30e2 100644 --- a/test/IDE/complete_member_decls_from_parent_decl_context.swift +++ b/test/IDE/complete_member_decls_from_parent_decl_context.swift @@ -83,9 +83,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1: End completions } @@ -100,9 +100,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInClassMethods1.Type#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): CodeCompletionInClassMethods1#})[#() -> Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): CodeCompletionInClassMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -116,9 +116,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1: End completions } @@ -130,9 +130,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1: End completions } @@ -184,9 +184,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1: End completions } @@ -201,9 +201,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInStructMethods1.Type#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): &CodeCompletionInStructMethods1#})[#() -> Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): &CodeCompletionInStructMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -217,9 +217,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1: End completions } @@ -241,9 +241,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceVar]/CurrNominal: aInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -264,9 +264,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_2-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc({#(self): &NestedInnerA#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticVar]/CurrNominal: aStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticMethod]/CurrNominal: aStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -281,9 +281,9 @@ struct NestedOuter1 { typealias ATestTypealias = #^NESTED_NOMINAL_DECL_A_3^# // NESTED_NOMINAL_DECL_A_3: Begin completions -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -343,9 +343,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceVar]/CurrNominal: bInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -369,9 +369,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_2-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc({#(self): &NestedInnerB#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticVar]/CurrNominal: bStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticMethod]/CurrNominal: bStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -389,9 +389,9 @@ struct NestedOuter1 { typealias BTestTypealias = #^NESTED_NOMINAL_DECL_B_3^# // NESTED_NOMINAL_DECL_B_3: Begin completions -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -461,9 +461,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceVar]/CurrNominal: cInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -479,9 +479,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_2-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc({#(self): &NestedInnerC#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticVar]/CurrNominal: cStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticMethod]/CurrNominal: cStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -491,9 +491,9 @@ func testOuterC() { typealias CTestTypealias = #^NESTED_NOMINAL_DECL_C_3^# // NESTED_NOMINAL_DECL_C_3: Begin completions -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index 1d376c8710a67..ef7accb15c7b2 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -92,7 +92,7 @@ protocol HasAssocWithSuperClassConstraint { } protocol HasAssocWithCompositionConstraint { associatedtype AssocWithCompositionConstraint: MyClass & MyProtocol - subscript(idx: T) -> AssocWithCompositionConstraint where T: Comparable { get } + subscript(idx: Int) -> AssocWithCompositionConstraint { get } } protocol HasAssocWithDefault { associatedtype AssocWithDefault = MyEnum @@ -102,6 +102,22 @@ protocol HasAssocWithConstraintAndDefault { associatedtype AssocWithConstraintAndDefault: MyProtocol = ConcreteMyProtocol func returnAssocWithConstraintAndDefault() -> AssocWithConstraintAndDefault } +protocol HasAssocWithAnyObjectConstraint { + associatedtype AssocWithAnyObjectConstraint: AnyObject & MyProtocol + func returnAssocWithAnyObjectConstraint() -> AssocWithAnyObjectConstraint +} +protocol HasAssocWithConstraintOnProto where Self.AssocWithConstraintOnProto : MyProtocol { + associatedtype AssocWithConstraintOnProto + func returnAssocWithConstraintOnProto() -> AssocWithConstraintOnProto +} +protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint == ConcreteMyProtocol { + associatedtype AssocWithSameTypeConstraint : MyProtocol + func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint +} +protocol HasAssocWithConformanceConstraintGeneric { + associatedtype AssocWithConformanceConstraintGeneric: MyProtocol + func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric +} class TestClass : HasAssocPlain, @@ -109,16 +125,23 @@ class TestClass : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestClass^# -// OVERRIDE: found code completion token OVERRIDE_[[BASETYPE:[A-Za-z0-9]+]] at // OVERRIDE: Begin completions -// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> [[BASETYPE]].AssocPlain {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> AssocPlain {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraint(fn: (Int) -> Int) -> some MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceVar]/Super: var valAssocWithSuperClassConstraint: some MyClass; -// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: T) -> some MyClass & MyProtocol where T : Comparable {|}; +// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: Int) -> some MyClass & MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithDefault() -> MyEnum {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintAndDefault() -> ConcreteMyProtocol {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithAnyObjectConstraint() -> some MyProtocol & AnyObject {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintOnProto() -> some MyProtocol {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric {|} // OVERRIDE: End completions } @@ -128,7 +151,11 @@ struct TestStruct : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestStruct^# } diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index 27110af69ab8e..4d85e3a36a40d 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -857,7 +857,7 @@ struct MissingAssoc: AssocAndMethod { func #^MISSING_ASSOC_1^# } // MISSING_ASSOC_1: Begin completions -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: MissingAssoc.T) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: MissingAssoc.U) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: MissingAssoc.V) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: T) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: U) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: V) {|}; // MISSING_ASSOC_1: End completions diff --git a/test/IDE/complete_override_typealias.swift b/test/IDE/complete_override_typealias.swift index 0485e4a279c70..dd0e77cc53474 100644 --- a/test/IDE/complete_override_typealias.swift +++ b/test/IDE/complete_override_typealias.swift @@ -8,8 +8,10 @@ protocol MyProtocol { associatedtype Result typealias Arg1 = Generic typealias Arg2 = Generic + typealias Func = () -> Int func foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2? arg6: Generic>>>) -> Result func bar() -> Result + func baz(arg: @escaping Func) } struct MyStruct1 : MyProtocol { @@ -29,17 +31,20 @@ struct MyStruct3 : MyProtocol { func #^OVERRIDE_3^# } -// OVERRIDE_1: Begin completions, 2 items -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct1.Arg1, arg2: MyStruct1.Arg2, arg3: MyStruct1.Arg2, arg4: [MyStruct1.Arg1], arg5: MyStruct1.Arg2?, arg6: Generic>>>) -> MyStruct1.Result {|}; -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> MyStruct1.Result {|}; +// OVERRIDE_1: Begin completions, 3 items +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_1: End completions -// OVERRIDE_2: Begin completions, 2 items -// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct2.Arg1, arg2: MyStruct2.Arg2, arg3: MyStruct2.Arg2, arg4: [MyStruct2.Arg1], arg5: MyStruct2.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_2: Begin completions, 3 items +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; // OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: bar() -> String {|}; +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_2: End completions -// OVERRIDE_3-DAG: Begin completions, 1 items -// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct3.Arg1, arg2: MyStruct3.Arg2, arg3: MyStruct3.Arg2, arg4: [MyStruct3.Arg1], arg5: MyStruct3.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Begin completions, 2 items +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_3-DAG: End completions diff --git a/test/IDE/complete_single_expression_return.swift b/test/IDE/complete_single_expression_return.swift index 8573a1ff1a3d8..1df478228eb2d 100644 --- a/test/IDE/complete_single_expression_return.swift +++ b/test/IDE/complete_single_expression_return.swift @@ -98,7 +98,7 @@ struct TestSingleExprClosureRetUnresolved { // TestSingleExprClosureRetUnresolved: Begin completions // TestSingleExprClosureRetUnresolved-NOT: notMine -// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExprClosure{{(Ret)?}}Unresolved.MyEnum#]; +// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprClosureRetUnresolved-NOT: notMine // TestSingleExprClosureRetUnresolved: End completions } @@ -263,7 +263,7 @@ struct TestSingleExprFuncUnresolved { // TestSingleExprFuncUnresolved: Begin completions // TestSingleExprFuncUnresolved-NOT: notMine -// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}FuncUnresolved.MyEnum#]; +// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprFuncUnresolved-NOT: notMine // TestSingleExprFuncUnresolved: End completions } @@ -373,7 +373,7 @@ struct TestSingleExprAccessorUnresolved { // TestSingleExprAccessorUnresolved: Begin completions // TestSingleExprAccessorUnresolved-NOT: notMine -// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Accessor{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprAccessorUnresolved-NOT: notMine // TestSingleExprAccessorUnresolved: End completions } @@ -525,7 +525,7 @@ struct TestSingleExprSubscriptUnresolved { // TestSingleExprSubscriptUnresolved: Begin completions // TestSingleExprSubscriptUnresolved-NOT: notMine -// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Subscript{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprSubscriptUnresolved-NOT: notMine // TestSingleExprSubscriptUnresolved: End completions } diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index 16a7b8323f948..37a3a5d039f8a 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -117,7 +117,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_4 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT_BOOL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_5 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_6 | %FileCheck %s -check-prefix=FOOSTRUCT_NODOT - +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_7 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_8 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL struct FooStruct { var instanceVar : Int @@ -605,6 +606,13 @@ func testGuardLetBinding5(x: FooStruct?) { func testGuardLetBinding5(x: FooStruct?) { guard let y = x, z = y#^GUARD_LET_BIND_6^# else {} } +func testGuardLetBinding7(x: FooStruct?) { + guard let boundVal = x, let other = #^GUARD_LET_BIND_7^# else {} +} +func testGuardLetBinding8(_ x: FooStruct?) { + guard let boundVal = x, let other = testGuardLetBinding8(#^GUARD_LET_BIND_8^#) else {} +} + // FOOSTRUCT_DOT: Begin completions // FOOSTRUCT_DOT-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]; @@ -623,3 +631,7 @@ func testGuardLetBinding5(x: FooStruct?) { // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .boolGen()[#Bool#]; // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .intGen()[#Int#]; // FOOSTRUCT_NODOT: End completions + +// FOOSTRUCT_LOCALVAL: Begin completions +// FOOSTRUCT_LOCALVAL-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: boundVal[#FooStruct#]; +// FOOSTRUCT_LOCALVAL: End completions diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index 93334e7f0e124..c3ffb661adffd 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -247,64 +247,64 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt @@ -671,9 +671,9 @@ class TestTypeInLocalVarInMemberFunc1 { } } // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: Begin completions -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#TestTypeInLocalVarInMemberFunc1.NestedStruct#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#TestTypeInLocalVarInMemberFunc1.NestedClass#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#TestTypeInLocalVarInMemberFunc1.NestedEnum#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: End completions @@ -911,6 +911,19 @@ extension VarBase1 { // VAR_BASE_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_BASE_1_TYPES: End completions +// VAR_BASE_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseNestedStruct[#BaseNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseNestedClass[#BaseNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseNestedEnum[#BaseNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseExtNestedStruct[#BaseExtNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseExtNestedClass[#BaseExtNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseExtNestedEnum[#BaseExtNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT: End completions + // VAR_BASE_1_NO_DOT_TYPES: Begin completions // From VarBase1 // VAR_BASE_1_NO_DOT_TYPES-DAG: Decl[Struct]/CurrNominal: .BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} @@ -987,6 +1000,29 @@ extension VarDerived1 { // VAR_DERIVED_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_DERIVED_1_TYPES: End completions +// VAR_DERIVED_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseNestedClass[#VarBase1.BaseNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseNestedEnum[#VarBase1.BaseNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseExtNestedStruct[#VarBase1.BaseExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseExtNestedClass[#VarBase1.BaseExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseExtNestedEnum[#VarBase1.BaseExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedNestedStruct[#DerivedNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedNestedClass[#DerivedNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedNestedEnum[#DerivedNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT: End completions + //===--- //===--- Test that we can complete based on user-provided type-identifier. //===--- diff --git a/test/IDE/complete_type_subscript.swift b/test/IDE/complete_type_subscript.swift index e93fcf59a96e6..5411068a60b6c 100644 --- a/test/IDE/complete_type_subscript.swift +++ b/test/IDE/complete_type_subscript.swift @@ -20,7 +20,7 @@ struct S1 { subscript(x: MyStruct#^PARAM_1^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct#^RETURN_1^# { } } -// MYSTRUCT_0: Keyword/None: .Type[#S1.MyStruct.Type#]; +// MYSTRUCT_0: Keyword/None: .Type[#MyStruct.Type#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_2 | %FileCheck %s -check-prefix=MYSTRUCT_1 @@ -30,8 +30,8 @@ struct S2 { subscript(x: MyStruct.#^PARAM_2^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct.#^RETURN_2^# { } } -// MYSTRUCT_1: Keyword/None: Type[#S2.MyStruct.Type#]; -// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#S2.MyStruct#]; +// MYSTRUCT_1: Keyword/None: Type[#MyStruct.Type#]; +// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#MyStruct#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index c19557f524fff..bbe71f44ea5ec 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -53,7 +53,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_1 | %FileCheck %s -check-prefix=WITH_LITERAL_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_2 | %FileCheck %s -check-prefix=WITH_LITERAL_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INVALID_1 @@ -443,6 +443,9 @@ func testWithLiteral3() { func takeEnum(thing: MyEnum, other: Double) {} func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) +// WITH_LITERAL_3: Begin completions, 1 items +// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: End completions } } } diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 48ded10a61b5f..6e58881b90b4f 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -452,7 +452,7 @@ class d0120_TestClassBase { } class d0121_TestClassDerived : d0120_TestClassBase { -// PASS_COMMON-LABEL: {{^}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} +// PASS_COMMON-LABEL: {{^}}@_inheritsConvenienceInitializers {{()?}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} required init() { super.init() } // PASS_COMMON-NEXT: {{^}} required init(){{$}} @@ -611,8 +611,8 @@ struct d0200_EscapedIdentifiers { // PASS_ONE_LINE_TYPEREPR-DAG: {{^}} typealias `protocol` = `class`{{$}} class `extension` : `class` {} -// PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} -// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}} +// PASS_ONE_LINE_TYPE-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} +// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : `class` {{{$}} // PASS_COMMON: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} @@ -748,7 +748,7 @@ class d0260_ExplodePattern_TestClassBase { } class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase { -// PASS_EXPLODE_PATTERN-LABEL: {{^}}class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} +// PASS_EXPLODE_PATTERN-LABEL: {{^}}@_inheritsConvenienceInitializers class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} override final var baseProp2: Int { get { @@ -791,13 +791,13 @@ class ClassWithInheritance2 : FooProtocol, BarProtocol {} // PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance2 : FooProtocol, BarProtocol {{{$}} class ClassWithInheritance3 : FooClass {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance3 : FooClass {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance3 : FooClass {{{$}} class ClassWithInheritance4 : FooClass, FooProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} class ClassWithInheritance6 : QuxProtocol, SubFooProtocol { typealias Qux = Int diff --git a/test/IDE/reconstruct_type_from_mangled_name.swift b/test/IDE/reconstruct_type_from_mangled_name.swift index 8e1ac96af60cd..f5ad1f0b6ff35 100644 --- a/test/IDE/reconstruct_type_from_mangled_name.swift +++ b/test/IDE/reconstruct_type_from_mangled_name.swift @@ -247,3 +247,9 @@ private func patatino(_ vers1: T, _ vers2: T) -> Bool { return vers1 < vers2; } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "OtherModule", OSX 10.13) +public struct MovedHere { + public func foo() {} +} diff --git a/test/IRGen/ELF-remove-autolink-section.swift b/test/IRGen/ELF-remove-autolink-section.swift index d74d51e10d867..3a2b30ff3c415 100644 --- a/test/IRGen/ELF-remove-autolink-section.swift +++ b/test/IRGen/ELF-remove-autolink-section.swift @@ -13,5 +13,15 @@ print("Hi from Swift!") // ELF: module asm ".section .swift1_autolink_entries,\220x80000000\22" + +// Find the metadata entry for the blacklisting of the metadata symbol +// Ensure that it is in the ASAN metadata + +// ELF-DAG: !llvm.asan.globals = !{ +// ELF-SAME: [[MD:![0-9]+]] +// ELF-SAME: } + +// ELF-DAG: [[MD]] = !{[37 x i8]* @_swift1_autolink_entries, null, null, i1 false, i1 true} + // SECTION: .swift1_autolink_entries // NOSECTION-NOT: .swift1_autolink_entries diff --git a/test/IRGen/associated_type_witness.swift b/test/IRGen/associated_type_witness.swift index 690c8eac25474..9342c8ba5addb 100644 --- a/test/IRGen/associated_type_witness.swift +++ b/test/IRGen/associated_type_witness.swift @@ -133,6 +133,37 @@ struct UsesVoid : HasSimpleAssoc { typealias Assoc = () } +// SR-11642: Failure to canonicalize type in associated type witness. +struct Validator { + let validatorFailureType: Any.Type +} + + +protocol ValidatorType { + associatedtype Data + associatedtype Failure + func validator() -> Validator +} + + +extension ValidatorType { + func validator() -> Validator { + .init(validatorFailureType: Failure.self) + } +} + + +// MARK: Failing example +extension Validator where T == String { + // GLOBAL: @"symbolic _____ySS__G 23associated_type_witness9ValidatorVAASSRszlE1VV7FailureV" + struct V: ValidatorType { + typealias Data = T // or String + + struct Failure {} + } +} + + // Protocol conformance descriptor for Computed : Assocked. // GLOBAL-LABEL: @"$s23associated_type_witness8ComputedVyxq_GAA8AssockedAAMc" = hidden constant // GLOBAL-SAME: i16 4, diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index 32ded99505976..0520bbd6cc7bf 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -215,7 +215,7 @@ public func testGetFunc() { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself) // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) +// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) // CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/IRGen/generic_metatypes.swift b/test/IRGen/generic_metatypes.swift index e454e0fd5c6c1..f43d5cfbfa266 100644 --- a/test/IRGen/generic_metatypes.swift +++ b/test/IRGen/generic_metatypes.swift @@ -145,5 +145,5 @@ func makeGenericMetatypes() { // CHECK-NOT: call void @llvm.lifetime.end // CHECK: ret %swift.metadata_response -// CHECK: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } -// CHECK: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" +// CHECK-DAG: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } +// CHECK-DAG: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" diff --git a/test/IRGen/lit.local.cfg b/test/IRGen/lit.local.cfg index ff48ffce548be..0597958565874 100644 --- a/test/IRGen/lit.local.cfg +++ b/test/IRGen/lit.local.cfg @@ -5,6 +5,10 @@ config.substitutions.insert(0, ('%build-irgen-test-overlays', '%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk %S/Inputs %S/Inputs/ObjectiveC.swift && ' '%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk %S/Inputs %S/Inputs/Foundation.swift -I %t')) +config.substitutions.insert(0, ('%build-irgen-test-overlays\(mock-sdk-directory: ([^)]+)\)', + SubstituteCaptures(r'%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk \1 \1/ObjectiveC.swift && ' + r'%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk \1 \1/Foundation.swift -I %t'))) + def get_target_os(): import re (run_cpu, run_vendor, run_os, run_version) = re.match('([^-]+)-([^-]+)-([^0-9]+)(.*)', config.variant_triple).groups() diff --git a/test/IRGen/original-defined-attr-linker-hide-ios.swift b/test/IRGen/original-defined-attr-linker-hide-ios.swift new file mode 100644 index 0000000000000..02ddaf7932d1f --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide-ios.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s + +// REQUIRES: OS=ios + +@available(iOS 5.0, OSX 10.10, *) +@_originallyDefinedIn(module: "OriginalModule", iOS 5.4, OSX 10.13) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK: $ld$hide$os5.0$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.1$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.2$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.3$_$s14OriginalModule6EntityVN +// CHECK-NOT: $ld$hide$os5.4$_$s14OriginalModule6EntityVN diff --git a/test/IRGen/original-defined-attr-linker-hide.swift b/test/IRGen/original-defined-attr-linker-hide.swift new file mode 100644 index 0000000000000..a6bb17141b875 --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-SAMEMAJOR --check-prefix=CHECK-DIFFMAJOR +// REQUIRES: OS=macosx + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-SAMEMAJOR: $ld$hide$os10.8$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR: $ld$hide$os10.9$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule6EntityVN + +@available(OSX 9.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct OldEntity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-DIFFMAJOR: $ld$hide$os9.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.13$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.30$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.8$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule9OldEntityVN diff --git a/test/IRGen/original-defined-attr.swift b/test/IRGen/original-defined-attr.swift index d586acb05d50d..65ce1ce378152 100644 --- a/test/IRGen/original-defined-attr.swift +++ b/test/IRGen/original-defined-attr.swift @@ -1,14 +1,17 @@ // RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-CURRENT --check-prefix=CHECK-CURRENT-%target-ptrsize // RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name OriginalModule | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-ORIGINAL --check-prefix=CHECK-ORIGINAL-%target-ptrsize +// REQUIRES: OS=macosx #if CURRENT_MODULE +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public struct Entity { public func addEntity(_ e: Entity) {} public func removeEntity(_ e: Entity) {} } +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public protocol Movable { func MovableFuncFoo() @@ -16,6 +19,7 @@ public protocol Movable { public protocol Unmoveable {} +@available(OSX 10.8, *) @_originallyDefinedIn(module: "OriginalModule", macOS 10.15) public class MovedClass: Movable, Unmoveable { public func MovableFuncFoo() {} diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..8c77d62e50823 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,33 @@ +// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +precedencegroup AssignmentPrecedence {} + +class Namespace {} + +class Zang { +} + +extension Namespace where T == Zang { + class ExtensionNonGeneric {} +} + +@inline(never) +func consume(_ t: T) { + Builtin.fixLifetime(t) +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[METADATA_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s4main9NamespaceCA2A4ZangCRszlE19ExtensionNonGenericCyAE_GMa"([[INT]] 0) #{{[0-9]+}} +// CHECK: [[METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[METADATA_RESPONSE]], 0 +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %5, %swift.type* [[METADATA]]) +// CHECK: } +func doit() { + consume( Namespace.ExtensionNonGeneric() ) +} + +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..4337bf632f36e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,41 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +fileprivate struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define internal swiftcc %swift.metadata_response @"$s4main5Value[[UNIQUE_ID_1]]VMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..1db12cbcb0991 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,43 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySi_GMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +final class Namespace { + struct Value { + let first: Arg + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: br i1 [[EQUAL_TYPES_1_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift new file mode 100644 index 0000000000000..13da1ee56a613 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -0,0 +1,23 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #0 { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast ([[INT]]* getelementptr inbounds (<{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>, <{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift new file mode 100644 index 0000000000000..42dff49c51270 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -0,0 +1,29 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift new file mode 100644 index 0000000000000..156d2e1539550 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -0,0 +1,45 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +protocol P {} +extension Int : P {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #12 +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift new file mode 100644 index 0000000000000..054f4cf84b7ea --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -0,0 +1,34 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK-NOT: @"$s4main5ValueVyAA7IntegerVGMf" +struct Value { + let first: First +} + +struct Integer : Hashable { + let value: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +func doit() { + consume( Value(first: Integer(value: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_CONFORMANCE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_CONFORMANCE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift new file mode 100644 index 0000000000000..6fc3aedf3621a --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -0,0 +1,39 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Outer { + let first: First +} + +struct Inner { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// TODO: Once prespecialization is done for generic arguments which are +// themselves generic (Outer>, here), a direct reference to +// the prespecialized metadata should be emitted here. +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5OuterVyAA5InnerVySiGGMD") #11 +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume( Outer(first: Inner(first: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..7447b96f1481a --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,41 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift new file mode 100644 index 0000000000000..f3b4dfd36c7a2 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -0,0 +1,48 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +protocol P {} +protocol Q {} +extension Int : P {} +extension Int : Q {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE_1:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: [[ERASED_TABLE_2:%[0-9]+]] = bitcast i8** %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift new file mode 100644 index 0000000000000..817344f003dca --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -0,0 +1,50 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift new file mode 100644 index 0000000000000..d363392ae0f82 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -0,0 +1,50 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +protocol P {} +protocol Q {} +protocol R {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift new file mode 100644 index 0000000000000..ac0d1129f8af7 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -0,0 +1,60 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift new file mode 100644 index 0000000000000..67efa8458ada3 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -0,0 +1,52 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift new file mode 100644 index 0000000000000..546b8cc53d5d7 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -0,0 +1,69 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sBi8_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift new file mode 100644 index 0000000000000..6e799b7585fab --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -0,0 +1,54 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external global %swift.full_type +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1TAAWP", i32 0, i32 0), i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +protocol T {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +extension Int : T {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift new file mode 100644 index 0000000000000..e7081cca80aab --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -0,0 +1,80 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + + +// CHECK: @"$sBi8_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys4Int8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss4Int8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi8_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$sBi64_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sBi64_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) + consume( Value(first: 13 as Int8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_5:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5]] +// CHECK: br i1 [[EQUAL_TYPES_5]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift new file mode 100644 index 0000000000000..447889caac128 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays(mock-sdk-directory: %S/../Inputs) +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// REQUIRES: objc_interop + +import Foundation + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +struct Value { + let value: T + + init(_ value: T) { + self.value = value + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5ValueVySo12NSDictionaryCGMD") #{{[0-9]+}} +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %12, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume(Value(NSDictionary())) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..8eb07e2ff33bf --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,46 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +final class Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..4fc44026d2c15 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -0,0 +1,46 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceO5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +enum Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceO5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..036f66e87f7d1 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -0,0 +1,46 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceV5ValueVySS_SiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +struct Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceV5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift new file mode 100644 index 0000000000000..00c7687d1ea0c --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -0,0 +1,31 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift new file mode 100644 index 0000000000000..7c78050b67d67 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -0,0 +1,45 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift new file mode 100644 index 0000000000000..06973e4e5bf3d --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -0,0 +1,57 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift new file mode 100644 index 0000000000000..a65955302f1db --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -0,0 +1,69 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift new file mode 100644 index 0000000000000..67000561d9fc9 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -0,0 +1,81 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift new file mode 100644 index 0000000000000..89c6302e01989 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -0,0 +1,91 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) + consume( Value(first: 13 as Int8, second: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_5_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5_1]] +// CHECK: [[EQUAL_TYPE_5_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_5_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_5_1]], [[EQUAL_TYPE_5_2]] +// CHECK: br i1 [[EQUAL_TYPES_5_2]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..9aa4da19582af --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,50 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGWV" = linkonce_odr hidden constant %swift.vwtable { i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwet" to i8*), i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwst" to i8*), [[INT]] {{[0-9]+}}, [[INT]] {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, align [[ALIGNMENT]] +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVySS_SiSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", %swift.type* @"$sSdN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +final class Namespace { + struct Value { + let first: First + let second: Second + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13, second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: [[ERASED_TYPE_3:%[0-9]+]] = bitcast %swift.type* %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: [[EQUAL_TYPE_1_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_3]] +// CHECK: [[EQUAL_TYPES_1_3:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_2]], [[EQUAL_TYPE_1_3]] +// CHECK: br i1 [[EQUAL_TYPES_1_3]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* [[ERASED_TYPE_3]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..64d762b608f13 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,42 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sB[[INT]]_WV" = external global i8*, align [[ALIGNMENT]] +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** @"$sB[[INT]]_WV", [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] +@frozen +public struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define{{[ protected]*}} swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/unmanaged_objc_throw_func.swift b/test/IRGen/unmanaged_objc_throw_func.swift index dcccf5174ba71..128b41b3999f7 100644 --- a/test/IRGen/unmanaged_objc_throw_func.swift +++ b/test/IRGen/unmanaged_objc_throw_func.swift @@ -18,7 +18,7 @@ import Foundation // CHECK-NEXT: store i{{32|64}} 1, i{{32|64}}* %._value, align {{[0-9]+}} // CHECK-NEXT: %[[T4:.+]] = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %[[T1]], %swift.type* @"$sSiN") // CHECK-NEXT: %[[T5:.+]] = bitcast %TSo7NSArrayC* %[[T4]] to %TSo10CFArrayRefa* - // CHECK-NEXT: call void asm sideeffect "", "r"(%TSo10CFArrayRefa* %[[T5]]) + // CHECK-NEXT: store %TSo10CFArrayRefa* %[[T5]] // CHECK-NEXT: call void @swift_bridgeObjectRelease(%swift.bridge* %[[T1]]) #{{[0-9]+}} // CHECK-NEXT: %[[T6:.+]] = bitcast %TSo10CFArrayRefa* %[[T5]] to i8* // CHECK-NEXT: call void @llvm.objc.release(i8* %[[T6]]) diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index c055b48ebff88..4d4a7cf018575 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,6 +205,8 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); +int (*getFunctionPointer_(void))(int); + struct FunctionPointerWrapper { fptr a; fptr b; @@ -214,6 +216,14 @@ typedef void (*fptr2)(int, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); +int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); + +typedef struct Dummy { + int x; +} Dummy; + +Dummy * (*getFunctionPointer3(void))(Dummy *); + //===--- // Unions //===--- diff --git a/test/ModuleInterface/force-load-autolink.swift b/test/ModuleInterface/force-load-autolink.swift new file mode 100644 index 0000000000000..3cef9dd75b7ef --- /dev/null +++ b/test/ModuleInterface/force-load-autolink.swift @@ -0,0 +1,8 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface %s -module-link-name Foo -enable-library-evolution -autolink-force-load +// RUN: %target-swift-frontend -compile-module-from-interface -o %t/Foo.swiftmodule %t/Foo.swiftinterface +// RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t -source-filename %s | %FileCheck %s + +public func foo() {} + +// CHECK: link library: Foo, force load: true \ No newline at end of file diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift new file mode 100644 index 0000000000000..950ad95402ba6 --- /dev/null +++ b/test/ModuleInterface/full-convention.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +public func f( + // CHECK: g: @convention(c, cType: "void (*)(void)") + g: @convention(c) () -> (), + + // CHECK: h0: @convention(c, cType: "int (*)(long long)") + h0: @convention(c) (Int64) -> Int32, + // CHECK: h1: @convention(c, cType: "int (*)(long long)") + h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, + + // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") + i0: @convention(c) (Int64, Int32) -> Optional>, + // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") + i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, + + // CHECK: p0: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p0: @convention(c) (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p1: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p1: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p2: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p2: @convention(c) (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void, + + // CHECK: p3: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p3: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void +) {} diff --git a/test/ModuleInterface/inherits-superclass-initializers-client.swift b/test/ModuleInterface/inherits-superclass-initializers-client.swift new file mode 100644 index 0000000000000..53880c5c89d7d --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers-client.swift @@ -0,0 +1,41 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init delegates through the subclasses correctly. + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: rm %t/Module.swiftmodule +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -module-name Module -enable-library-evolution +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +import Module + +_ = Base() +// CHECK: secret init from Base + +_ = Sub() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +_ = SubSub() +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +test() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK-NOT: public init diff --git a/test/ModuleInterface/inherits-superclass-initializers.swift b/test/ModuleInterface/inherits-superclass-initializers.swift new file mode 100644 index 0000000000000..e2489d58a936e --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers.swift @@ -0,0 +1,60 @@ +// Note: this test has a client: inherits-superclass-initializers-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class Base { +open class Base { + // CHECK-NEXT: public init(arg: Swift.Int) + public init(arg: Int) { + print("public init from Base") + } + // CHECK-NOT: init(secret: Swift.Int) + internal init(secret: Int) { + print("secret init from Base") + } + + // CHECK: convenience public init() + public convenience init() { + self.init(secret: 4) + } + +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class Sub : Module.Base { +public class Sub : Base { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from Sub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from Sub") + super.init(secret: secret) + } +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class SubSub : Module.Sub { +public class SubSub: Sub { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from SubSub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from SubSub") + super.init(secret: secret) + } +// CHECK: } +} + +@inlinable public func test() { + _ = Sub() + _ = SubSub() +} diff --git a/test/ModuleInterface/member-typealias.swift b/test/ModuleInterface/member-typealias.swift new file mode 100644 index 0000000000000..6d8f2182c2cd6 --- /dev/null +++ b/test/ModuleInterface/member-typealias.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name MyModule | %FileCheck %s --check-prefix CHECK + +public struct MyStruct { +// CHECK-LABEL: public struct MyStruct { + public typealias AliasT = T + public typealias AliasInt = Int + + public func foo(x: AliasInt) -> AliasT { fatalError() } +// CHECK: public func foo(x: MyModule.MyStruct.AliasInt) -> MyModule.MyStruct.AliasT +} + +public class MyBase { + public typealias AliasU = U + public typealias AliasInt = Int +} + +public class MyDerived: MyBase { +// CHECK-LABEL: public class MyDerived : MyModule.MyBase { + public func bar(x: AliasU) -> AliasInt { fatalError() } +// CHECK: public func bar(x: MyModule.MyDerived.AliasU) -> MyModule.MyDerived.AliasInt +} diff --git a/test/ModuleInterface/non-public-designated-inits-client.swift b/test/ModuleInterface/non-public-designated-inits-client.swift new file mode 100644 index 0000000000000..a0e84cb52cbf5 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits-client.swift @@ -0,0 +1,26 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init cannot be called. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck %S/non-public-designated-inits.swift -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/Module.swiftmodule %S/non-public-designated-inits.swift -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +import Module + +open class B : A { + var x: Int + + public override init(_ x: Int) { + self.x = x + super.init(x) + } +} + +print(B(hi: ())) // expected-error {{cannot convert value of type '()' to expected argument type 'Int'}} +// expected-error @-1 {{extraneous argument label 'hi:' in call}} diff --git a/test/ModuleInterface/non-public-designated-inits.swift b/test/ModuleInterface/non-public-designated-inits.swift new file mode 100644 index 0000000000000..c8b00df92e677 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits.swift @@ -0,0 +1,21 @@ +// Note: This test has a client: non-public-designated-inits-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class A { +open class A { + // This is a non-public designated init, which means the convenience + // init should not be inheritable. + init() {} + + // CHECK-NEXT: public init(_: Swift.Int) + public init(_: Int) {} + + // CHECK-NEXT: convenience public init(hi: ()) + public convenience init(hi: ()) { self.init() } + +// CHECK: } +} diff --git a/test/ModuleInterface/nsmanaged-attr.swift b/test/ModuleInterface/nsmanaged-attr.swift index c199e163910fe..2312c8a926294 100644 --- a/test/ModuleInterface/nsmanaged-attr.swift +++ b/test/ModuleInterface/nsmanaged-attr.swift @@ -16,7 +16,7 @@ import CoreData import Foundation -// CHECK: @objc public class MyObject : CoreData.NSManagedObject { +// CHECK: @objc @_inheritsConvenienceInitializers public class MyObject : CoreData.NSManagedObject { public class MyObject: NSManagedObject { // CHECK: @objc @NSManaged dynamic public var myVar: Swift.String { // CHECK-NEXT: @objc get diff --git a/test/ModuleInterface/originally-defined-attr.swift b/test/ModuleInterface/originally-defined-attr.swift index 79bf27b7cc27c..0940414f57e32 100644 --- a/test/ModuleInterface/originally-defined-attr.swift +++ b/test/ModuleInterface/originally-defined-attr.swift @@ -9,6 +9,7 @@ // RUN: %FileCheck %s < %t/printed-module.txt // CHECK: @_originallyDefinedIn(module: "another", OSX 13.13) +@available(OSX 10.8, *) @_originallyDefinedIn(module: "another", OSX 13.13) public protocol SimpleProto { } @@ -16,6 +17,7 @@ public protocol SimpleProto { } // CHECK: @_originallyDefinedIn(module: "another_original", OSX 2.0) // CHECK: @_originallyDefinedIn(module: "another_original", iOS 3.0) // CHECK: @_originallyDefinedIn(module: "another_original", watchOS 4.0) +@available(tvOS 0.7, OSX 1.1, iOS 2.1, watchOS 3.2, *) @_originallyDefinedIn(module: "original", tvOS 1.0) @_originallyDefinedIn(module: "another_original", OSX 2.0, iOS 3.0, watchOS 4.0) public struct SimpleStruct {} diff --git a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h index f018637dc7ebb..86d93cc55e0d0 100644 --- a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h +++ b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h @@ -26,6 +26,8 @@ // Don't conform to the protocol; that loads all protocol members. @interface SimpleDoer +- (instancetype)initWithValue: (int)value; + // These are names we're hoping don't interfere with Doer, above. + (SimpleDoer*)Doer; + (SimpleDoer*)DoerOfNoWork; @@ -107,4 +109,14 @@ @interface SimpleDoerSubclass : SimpleDoer - (void)simplyDoSomeWorkWithSpeed:(int)s thoroughness:(int)t NS_SWIFT_NAME(simplyDoVeryImportantWork(speed:thoroughness:)); + +- (void)exuberantlyGoForWalk; +- (void)exuberantlyTakeNap; +- (void)exuberantlyEatMeal; +- (void)exuberantlyTidyHome; +- (void)exuberantlyCallFamily; +- (void)exuberantlySingSong; +- (void)exuberantlyReadBook; +- (void)exuberantlyAttendLecture; +- (void)exuberantlyWriteLetter; @end diff --git a/test/NameBinding/name_lookup.swift b/test/NameBinding/name_lookup.swift index 0e443d4e6d87c..7fabd37ba89a4 100644 --- a/test/NameBinding/name_lookup.swift +++ b/test/NameBinding/name_lookup.swift @@ -553,9 +553,9 @@ default: } func foo() { - _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'}}{{14-17=one}} - _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'}}{{14-17=two}} - _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'}}{{14-25=oneTwoThree}} + _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'?}}{{14-17=one}} + _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'?}}{{14-17=two}} + _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'?}}{{14-25=oneTwoThree}} } enum MyGenericEnum { @@ -564,8 +564,8 @@ enum MyGenericEnum { } func foo1() { - _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'}}{{26-29=one}} - _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'}}{{26-32=oneTwo}} + _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'?}}{{26-29=one}} + _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'?}}{{26-32=oneTwo}} } // SR-4082 diff --git a/test/NameBinding/named_lazy_member_loading_objc_category.swift b/test/NameBinding/named_lazy_member_loading_objc_category.swift index c3a45c9a41114..db5932d19630c 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_category.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_category.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -30' %t/stats-pre %t/stats-post import NamedLazyMembers diff --git a/test/NameBinding/named_lazy_member_loading_objc_interface.swift b/test/NameBinding/named_lazy_member_loading_objc_interface.swift index c3d9be7d1a6b5..d4201213bc328 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_interface.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_interface.swift @@ -8,17 +8,23 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities <= -1' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities <= -10' %t/stats-pre %t/stats-post import NamedLazyMembers -public func bar(d: SimpleDoerSubclass) { - let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) +public func bar() { + let d = SimpleDoerSubclass(value: 123)! + let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) } -public func foo(d: SimpleDoer) { +public func foo() { + let d = SimpleDoer(value: 123)! let _ = d.simplyDoSomeWork() let _ = d.simplyDoSomeWork(withSpeed:10) let _ = d.simplyDoVeryImportantWork(speed:10, thoroughness:12) let _ = d.simplyDoSomeWorkWithSpeed(speed:10, levelOfAlacrity:12) } + +// Make sure that simply subclassing an imported subclass doesn't page in all +// members. +class MostDoerSubclass : SimpleDoerSubclass {} diff --git a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift index 65f66bb372e1e..94607c52b675e 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -9' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -40' %t/stats-pre %t/stats-post import NamedLazyMembers diff --git a/test/Parse/ConditionalCompilation/switch_case.swift b/test/Parse/ConditionalCompilation/switch_case.swift index 822fa94f99674..941de52907719 100644 --- a/test/Parse/ConditionalCompilation/switch_case.swift +++ b/test/Parse/ConditionalCompilation/switch_case.swift @@ -209,6 +209,7 @@ func foo(x: E, intVal: Int) { fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'val'}} #if ENABLE_C case let val: + _ = val break #endif case 2: diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 3d51e63098b5f..188634808dfac 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -50,8 +50,8 @@ let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be fo func ct1() -> () { print("") } -let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1 -let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}} +let ct1ref0 : @convention(c, cType: "void (*)(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void (*)(void)") = ct1 // expected-error{{expected type}} let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/Parse/confusables.swift b/test/Parse/confusables.swift index 8952a19f29d59..14a72ad943034 100644 --- a/test/Parse/confusables.swift +++ b/test/Parse/confusables.swift @@ -7,9 +7,9 @@ let number⁚ Int // expected-note {{operator '⁚' contains possibly confused characters; did you mean to use ':'?}} {{11-14=:}} // expected-warning @+3 2 {{integer literal is unused}} -// expected-error @+2 2 {{invalid character in source file}} +// expected-error @+2 {{invalid character in source file}} // expected-error @+1 {{consecutive statements on a line must be separated by ';'}} -5 ‒ 5 // expected-note 2 {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{3-6=-}} +5 ‒ 5 // expected-note {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{3-6=-}} // expected-error @+2 {{use of unresolved identifier 'ꝸꝸꝸ'}} // expected-error @+1 {{expected ',' separator}} diff --git a/test/Parse/identifiers.swift b/test/Parse/identifiers.swift index 264b6243a6586..b0ad8fe398c50 100644 --- a/test/Parse/identifiers.swift +++ b/test/Parse/identifiers.swift @@ -26,7 +26,7 @@ func s̈pin̈al_tap̈() {} () // expected-error{{invalid character in source file}} {{1-4= }} // Placeholders are recognized as identifiers but with error. -func <#some name#>() {} // expected-error 2 {{editor placeholder in source file}} +func <#some name#>() {} // expected-error {{editor placeholder in source file}} // Keywords as identifiers class switch {} // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{7-13=`switch`}} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index d32ab35273643..39cadef2359ec 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -333,6 +333,6 @@ case (_?)?: break // expected-warning {{case is already handled by previous patt let (responseObject: Int?) = op1 // expected-error @-1 {{expected ',' separator}} {{25-25=,}} // expected-error @-2 {{expected pattern}} -// expected-error @-3 {{expression type 'Int?' is ambiguous without more context}} +// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}} diff --git a/test/Parse/original_defined_in_attr.swift b/test/Parse/original_defined_in_attr.swift index 634132b7ac933..03a43dbb3bce1 100644 --- a/test/Parse/original_defined_in_attr.swift +++ b/test/Parse/original_defined_in_attr.swift @@ -1,12 +1,13 @@ // RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx -@_originallyDefinedIn(module: "foo", OSX 13.13) +@_originallyDefinedIn(module: "foo", OSX 13.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} func foo() {} @_originallyDefinedIn(modulename: "foo", OSX 13.13) // expected-error {{expected 'module: "original"' in the first argument to @_originallyDefinedIn}} func foo1() {} -@_originallyDefinedIn(module: "foo", OSX 13.13.3) // expected-warning {{@_originallyDefinedIn only uses major and minor version number}} +@_originallyDefinedIn(module: "foo", OSX 13.13.3) // expected-warning {{@_originallyDefinedIn only uses major and minor version number}} expected-error {{need @available attribute for @_originallyDefinedIn}} class ToplevelClass {} @_originallyDefinedIn(module: "foo") // expected-error {{expected at least one platform version in @_originallyDefinedIn}} diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 13d911cd15536..c7503c2c97dca 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -619,9 +619,11 @@ class WrongInheritanceClause6(Int {} class WrongInheritanceClause7(Int where T:AnyObject {} // [swift-crashes 078] parser crash on invalid cast in sequence expr -Base=1 as Base=1 // expected-error {{cannot convert value of type 'Int' to type 'Base' in coercion}} - - +Base=1 as Base=1 // expected-error{{cannot convert value of type 'Int' to type 'Base' in coercion}} +// expected-error@-1 {{cannot assign to immutable expression of type 'Base.Type'}} +// expected-error@-2 {{cannot assign to immutable expression of type 'Base'}} +// expected-error@-3 {{cannot assign value of type '()' to type 'Base.Type'}} +// expected-error@-4 {{cannot assign value of type 'Int' to type 'Base'}} // Parser hangs at swift::Parser::parseType public enum TestA { @@ -754,7 +756,7 @@ let curlyQuotes2 = “hello world!" // compiler should recover better from "unicode Specials" characters -let tryx = 123 // expected-error 2 {{invalid character in source file}} {{5-8= }} +let tryx = 123 // expected-error {{invalid character in source file}} {{5-8= }} // Malformed Swift Enums crash playground service diff --git a/test/Parse/super.swift b/test/Parse/super.swift index 52ffa73cf7f52..c4317a4bf18b8 100644 --- a/test/Parse/super.swift +++ b/test/Parse/super.swift @@ -4,8 +4,8 @@ class B { var foo: Int func bar() {} - init() {} - init(x: Int) {} + init() {} // expected-note {{found this candidate}} + init(x: Int) {} // expected-note {{found this candidate}} subscript(x: Int) -> Int { get {} @@ -38,7 +38,8 @@ class D : B { super.foo.bar // expected-error {{value of type 'Int' has no member 'bar'}} super.bar // expected-error {{expression resolves to an unused function}} super.bar() - super.init // expected-error{{'super.init' cannot be called outside of an initializer}} + // FIXME: should also say "'super.init' cannot be referenced outside of an initializer" + super.init // expected-error{{no exact matches in call to initializer}} super.init() // expected-error{{'super.init' cannot be called outside of an initializer}} super.init(0) // expected-error{{'super.init' cannot be called outside of an initializer}} // expected-error {{missing argument label 'x:' in call}} super[0] // expected-error {{expression resolves to an unused subscript}} diff --git a/test/Parse/type_expr.swift b/test/Parse/type_expr.swift index 87c9cd955023a..4384e823852fb 100644 --- a/test/Parse/type_expr.swift +++ b/test/Parse/type_expr.swift @@ -265,7 +265,7 @@ protocol P2 {} protocol P3 {} func compositionType() { _ = P1 & P2 // expected-error {{expected member name or constructor call after type name}} expected-note{{use '.self'}} {{7-7=(}} {{14-14=).self}} - _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} expected-note {{overloads}} + _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} _ = (P1 & P2).self // Ok. _ = (P1 & (P2)).self // FIXME: OK? while `typealias P = P1 & (P2)` is rejected. _ = (P1 & (P2, P3)).self // expected-error {{non-protocol, non-class type '(P2, P3)' cannot be used within a protocol-constrained type}} diff --git a/test/Profiler/coverage_function_builder.swift b/test/Profiler/coverage_function_builder.swift index a08237f268eb8..a3cfb30c139ba 100644 --- a/test/Profiler/coverage_function_builder.swift +++ b/test/Profiler/coverage_function_builder.swift @@ -13,7 +13,7 @@ struct Summer { // CHECK-LABEL: sil_coverage_map {{.*}} "$s24coverage_functon_builder5test0SiyF" @Summer func test0() -> Int { - // CHECK: [[@LINE-1]]:21 -> [[@LINE+3]]:2 : 0 + // CHECK: [[@LINE-1]]:21 -> [[@LINE+2]]:5 : 0 18 12 } @@ -21,7 +21,7 @@ func test0() -> Int { // CHECK-LABEL: sil_coverage_map {{.*}} "$s24coverage_functon_builder5test1SiyF" @Summer func test1() -> Int { - // CHECK: [[@LINE-1]]:21 -> [[@LINE+7]]:2 : 0 + // CHECK: [[@LINE-1]]:21 -> [[@LINE+6]]:4 : 0 18 12 if 7 < 23 { diff --git a/test/Prototypes/Algorithms.swift b/test/Prototypes/Algorithms.swift index 35f690df2dee3..24d48fa2b14ca 100644 --- a/test/Prototypes/Algorithms.swift +++ b/test/Prototypes/Algorithms.swift @@ -613,6 +613,11 @@ extension Collection { /// The collection must already be partitioned according to the /// predicate, as if `self.partition(by: predicate)` had already /// been called. + /// + /// - Efficiency: At most log(N) invocations of `predicate`, where + /// N is the length of `self`. At most log(N) index offsetting + /// operations if `self` conforms to `RandomAccessCollection`; + /// at most N such operations otherwise. func partitionPoint( where predicate: (Element) throws -> Bool ) rethrows -> Index { diff --git a/test/Reflection/typeref_lowering.swift b/test/Reflection/typeref_lowering.swift index 4f076bf71d145..750b2af38eeb1 100644 --- a/test/Reflection/typeref_lowering.swift +++ b/test/Reflection/typeref_lowering.swift @@ -1039,9 +1039,9 @@ // CHECK-64-NEXT: (field name=empty offset=0 // CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-64-NEXT: (field name=noPayload offset=0 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=sillyNoPayload offset=1 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=singleton offset=8 // CHECK-64-NEXT: (reference kind=strong refcounting=native)) // CHECK-64-NEXT: (field name=singlePayload offset=16 diff --git a/test/Runtime/demangleToMetadataMovedSymbols.swift b/test/Runtime/demangleToMetadataMovedSymbols.swift new file mode 100644 index 0000000000000..397d51701d736 --- /dev/null +++ b/test/Runtime/demangleToMetadataMovedSymbols.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -parse-stdlib %s -module-name main -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import Swift +import StdlibUnittest + +let DemangleToMetadataMovedSymbolsTests = TestSuite("DemangleToMetadataMovedSymbols") + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +struct MovedS { + struct Nested { } +} + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +enum MovedE { case e } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "bar", OSX 10.13) +class MovedC {} + +DemangleToMetadataMovedSymbolsTests.test("Moved Nominals") { + // Simple Struct + expectEqual(type(of: MovedS()), _typeByName("3foo6MovedSV")!) + expectNil(_typeByName("4main6MovedSV")) + + // Simple Enum + expectEqual(type(of: MovedE.e), _typeByName("3foo6MovedEO")!) + expectNil(_typeByName("4main6MovedEO")) + + // Nested struct + expectEqual(type(of: MovedS.Nested()), _typeByName("3foo6MovedSV6NestedV")!) + expectNil(_typeByName("4main6MovedSV6NestedV")) + + // Simple Class + expectEqual(type(of: MovedC()), _typeByName("3bar6MovedCC")!) + expectNil(_typeByName("4main6MovedCC")) +} + +runAllTests() diff --git a/test/SIL/ownership-verifier/definite_init.sil b/test/SIL/ownership-verifier/definite_init.sil index 4a8e869f03b6a..edce0907eed35 100644 --- a/test/SIL/ownership-verifier/definite_init.sil +++ b/test/SIL/ownership-verifier/definite_init.sil @@ -32,17 +32,17 @@ sil @makesInt : $@convention(thin) () -> Builtin.Int32 //} sil [ossa] @used_by_inout : $@convention(thin) (Builtin.Int32) -> (Builtin.Int32, Builtin.Int32) { bb0(%0 : $Builtin.Int32): - %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Builtin.Int32 - store %0 to [trivial] %1 : $*Builtin.Int32 - %3 = load [trivial] %1 : $*Builtin.Int32 - %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () - %6 = apply %5(%1) : $@convention(thin) (@inout Builtin.Int32) -> () - %7 = load [trivial] %1 : $*Builtin.Int32 - %8 = tuple (%3 : $Builtin.Int32, %7 : $Builtin.Int32) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } - return %8 : $(Builtin.Int32, Builtin.Int32) + %2 = alloc_box $<τ_0_0> { var τ_0_0 } + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %0 to [trivial] %4 : $*Builtin.Int32 + %5 = load [trivial] %4 : $*Builtin.Int32 + %6 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () + %7 = apply %6(%4) : $@convention(thin) (@inout Builtin.Int32) -> () + %8 = load [trivial] %4 : $*Builtin.Int32 + %9 = tuple (%5 : $Builtin.Int32, %8 : $Builtin.Int32) + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + return %9 : $(Builtin.Int32, Builtin.Int32) } struct AddressOnlyStruct { @@ -57,24 +57,24 @@ sil @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct sil [ossa] @call_struct_return_function : $@convention(thin) () -> Builtin.Int32 { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Builtin.Int32 - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Builtin.Int32 } sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -87,8 +87,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @assign_test_trivial : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Builtin.Int32 + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -97,7 +97,7 @@ bb0(%0 : $Builtin.Int32): assign %0 to %1 : $*Builtin.Int32 %2 = load [trivial] %1 : $*Builtin.Int32 - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Builtin.Int32 } @@ -108,29 +108,28 @@ bb0: // lone store), the second becomes an assignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass %4 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %4 to %c : $*SomeClass %8 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %8 to %c : $*SomeClass destroy_addr %c : $*SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() } - sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %1 to %2 : $*T copy_addr [take] %1 to %2 : $*T copy_addr %2 to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -138,8 +137,8 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @assign_test_unowned : $@convention(thin) () -> () { bb0: %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -155,14 +154,12 @@ bb0: destroy_value %8 : $SomeClass destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() } - - struct ContainsNativeObject { var x : Builtin.Int32 var y : Builtin.NativeObject @@ -171,12 +168,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -337,8 +334,8 @@ sil @superinit : $@convention(method) (@owned RootClassWithIVars) -> @owned Root sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -359,7 +356,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -467,10 +464,10 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -478,12 +475,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -502,8 +499,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Builtin.Int32) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %8 = load_borrow %3 : $*DerivedClassWithIVars @@ -523,7 +520,7 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %16 = unconditional_checked_cast %15 : $RootClassWithIVars to DerivedClassWithIVars assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SIL/ownership-verifier/undef.sil b/test/SIL/ownership-verifier/undef.sil index 74d53def70f34..ebf5f138969ab 100644 --- a/test/SIL/ownership-verifier/undef.sil +++ b/test/SIL/ownership-verifier/undef.sil @@ -20,9 +20,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%1 = mark_uninitialized [var] undef : $Klass // CHECK-NEXT: Operand Ownership Map: @@ -36,9 +36,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%4 = struct $MyInt (undef : $Builtin.Int32) // CHECK-NEXT: Operand Ownership Map: diff --git a/test/SILGen/access_marker_gen.swift b/test/SILGen/access_marker_gen.swift index 43d8761643695..a4608c0227149 100644 --- a/test/SILGen/access_marker_gen.swift +++ b/test/SILGen/access_marker_gen.swift @@ -124,9 +124,9 @@ func testClassLetProperty(c: C) -> Int { // CHECK-LABEL: sil hidden [ossa] @$s17access_marker_gen20testClassLetProperty1cSiAA1CC_tF : $@convention(thin) (@guaranteed C) -> Int { // CHECK: bb0(%0 : @guaranteed $C): // CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $C, #C.z -// CHECK-NOT: begin_access -// CHECK: %{{.*}} = load [trivial] [[ADR]] : $*Int -// CHECK-NOT: end_access +// CHECK: [[ADR_ACCESS:%.*]] = begin_access [read] [unsafe] [[ADR]] +// CHECK: %{{.*}} = load [trivial] [[ADR_ACCESS]] : $*Int +// CHECK: end_access [[ADR_ACCESS]] // CHECK-NOT: destroy_value %0 : $C // CHECK: return %{{.*}} : $Int // CHECK-LABEL: } // end sil function '$s17access_marker_gen20testClassLetProperty1cSiAA1CC_tF' diff --git a/test/SILGen/builtins.swift b/test/SILGen/builtins.swift index 88f3dd656917f..fa7792c4c3aab 100644 --- a/test/SILGen/builtins.swift +++ b/test/SILGen/builtins.swift @@ -360,6 +360,23 @@ func projectTailElems(h: Header, ty: T.Type) -> Builtin.RawPointer { } // CHECK: } // end sil function '$s8builtins16projectTailElems1h2tyBpAA6HeaderC_xmtlF' +// Make sure we borrow if this is owned. +// +// CHECK-LABEL: sil hidden [ossa] @$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F +func projectTailElemsOwned(h: __owned Header, ty: T.Type) -> Builtin.RawPointer { + // CHECK: bb0([[ARG1:%.*]] : @owned $Header + // CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] + // CHECK: [[TA:%.*]] = ref_tail_addr [[BORROWED_ARG1]] : $Header + // -- Once we have passed the address through a2p, we no longer provide any guarantees. + // -- We still need to make sure that the a2p itself is in the borrow site though. + // CHECK: [[A2P:%.*]] = address_to_pointer [[TA]] + // CHECK: end_borrow [[BORROWED_ARG1]] + // CHECK: destroy_value [[ARG1]] + // CHECK: return [[A2P]] + return Builtin.projectTailElems(h, ty) +} +// CHECK: } // end sil function '$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F' + // CHECK-LABEL: sil hidden [ossa] @$s8builtins11getTailAddr{{[_0-9a-zA-Z]*}}F func getTailAddr(start: Builtin.RawPointer, i: Builtin.Word, ty1: T1.Type, ty2: T2.Type) -> Builtin.RawPointer { // CHECK: [[P2A:%.*]] = pointer_to_address %0 diff --git a/test/SILGen/foreach.swift b/test/SILGen/foreach.swift index cfb7b67d61241..5c67eff05efd7 100644 --- a/test/SILGen/foreach.swift +++ b/test/SILGen/foreach.swift @@ -108,15 +108,15 @@ func trivialStructBreak(_ xx: [Int]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[Int]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[GET_ELT_STACK:%.*]] = alloc_stack $Optional // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[GET_ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[GET_ELT_STACK]], [[WRITE]]) // CHECK: [[IND_VAR:%.*]] = load [trivial] [[GET_ELT_STACK]] // CHECK: switch_enum [[IND_VAR]] : $Optional, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // @@ -208,15 +208,15 @@ func existentialBreak(_ xx: [P]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array

// CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array

, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[P]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional

// CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional

, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: @@ -368,15 +368,15 @@ func genericStructBreak(_ xx: [GenericStruct]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array> // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array>, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[GenericStruct]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional> // CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator>> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional>, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: diff --git a/test/SILGen/guaranteed_self.swift b/test/SILGen/guaranteed_self.swift index 3a0c807867cc8..2395e824c282e 100644 --- a/test/SILGen/guaranteed_self.swift +++ b/test/SILGen/guaranteed_self.swift @@ -451,11 +451,14 @@ class LetFieldClass { // CHECK-LABEL: sil hidden [ossa] @$s15guaranteed_self13LetFieldClassC10letkMethod{{[_0-9a-zA-Z]*}}F : $@convention(method) (@guaranteed LetFieldClass) -> () { // CHECK: bb0([[CLS:%.*]] : @guaranteed $LetFieldClass): // CHECK: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] + // CHECK-NEXT: end_access [[KRAKEN_ADDR_ACCESS]] // CHECK-NEXT: [[KRAKEN_METH:%.*]] = class_method [[KRAKEN]] // CHECK-NEXT: apply [[KRAKEN_METH]]([[KRAKEN]]) // CHECK: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] // CHECK: [[REBORROWED_KRAKEN:%.*]] = begin_borrow [[KRAKEN]] // CHECK: [[DESTROY_SHIP_FUN:%.*]] = function_ref @$s15guaranteed_self11destroyShipyyAA6KrakenCF : $@convention(thin) (@guaranteed Kraken) -> () // CHECK-NEXT: apply [[DESTROY_SHIP_FUN]]([[REBORROWED_KRAKEN]]) @@ -464,7 +467,9 @@ class LetFieldClass { // CHECK-NEXT: [[KRAKEN_BOX:%.*]] = alloc_box ${ var Kraken } // CHECK-NEXT: [[PB:%.*]] = project_box [[KRAKEN_BOX]] // CHECK-NEXT: [[KRAKEN_ADDR:%.*]] = ref_element_addr [[CLS]] : $LetFieldClass, #LetFieldClass.letk - // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[KRAKEN_ADDR]] + // CHECK-NEXT: [[KRAKEN:%.*]] = load [copy] [[KRAKEN_ADDR_ACCESS]] + // CHECK-NEXT: end_access [[KRAKEN_ADDR_ACCESS]] // CHECK-NEXT: store [[KRAKEN]] to [init] [[PB]] // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[PB]] : $*Kraken // CHECK-NEXT: [[KRAKEN_COPY:%.*]] = load [copy] [[READ]] diff --git a/test/SILGen/ivar_destroyer.swift b/test/SILGen/ivar_destroyer.swift index ed49bda2c33f8..8e0846e2dcbc8 100644 --- a/test/SILGen/ivar_destroyer.swift +++ b/test/SILGen/ivar_destroyer.swift @@ -29,7 +29,9 @@ class DerivedClassWithNonTrivialProperties : RootClassWithoutProperties { // CHECK: bb0(%0 : @guaranteed $DerivedClassWithNonTrivialProperties): // CHECK-NEXT: debug_value %0 // CHECK-NEXT: [[Z_ADDR:%.*]] = ref_element_addr %0 -// CHECK-NEXT: destroy_addr [[Z_ADDR]] +// CHECK-NEXT: [[Z_ADDR_DEINIT_ACCESS:%.*]] = begin_access [deinit] [static] [[Z_ADDR]] +// CHECK-NEXT: destroy_addr [[Z_ADDR_DEINIT_ACCESS]] +// CHECK-NEXT: end_access [[Z_ADDR_DEINIT_ACCESS]] // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] diff --git a/test/SILGen/lifetime.swift b/test/SILGen/lifetime.swift index d0e433bb92247..e797724c3620f 100644 --- a/test/SILGen/lifetime.swift +++ b/test/SILGen/lifetime.swift @@ -263,7 +263,7 @@ struct Daleth { } class He { - + // -- default allocator: // CHECK-LABEL: sil hidden [exact_self_class] [ossa] @$s8lifetime2HeC{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@thick He.Type) -> @owned He { // CHECK: bb0({{%.*}} : $@thick He.Type): @@ -292,7 +292,7 @@ struct Waw { var b:Val // -- loadable value initializer with tuple destructuring: - // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw // CHECK: bb0([[A0:%.*]] : @owned $Ref, [[A1:%.*]] : $Val, [[B:%.*]] : $Val, {{%.*}} : $@thin Waw.Type): // CHECK-NEXT: [[A:%.*]] = tuple ([[A0]] : {{.*}}, [[A1]] : {{.*}}) // CHECK-NEXT: [[RET:%.*]] = struct $Waw ([[A]] : {{.*}}, [[B]] : {{.*}}) @@ -513,7 +513,7 @@ class Foo { x = chi.intify() } - + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3FooCfd : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject deinit { @@ -526,13 +526,19 @@ class Foo { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #Foo.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value z // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // -- return back this // CHECK: [[PTR:%.*]] = unchecked_ref_cast [[THIS]] : $Foo to $Builtin.NativeObject // CHECK: [[PTR_OWNED:%.*]] = unchecked_ownership_conversion [[PTR]] : $Builtin.NativeObject, @guaranteed to @owned @@ -567,7 +573,7 @@ class FooSubclass : Foo { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: end_borrow [[BORROWED_PTR]] // CHECK: return [[PTR]] - + deinit { bar() @@ -586,18 +592,22 @@ class ImplicitDtor { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // CHECK: return } class ImplicitDtorDerived : ImplicitDtor { var z:T - init(z : T) { - super.init() + init(z : T) { + super.init() self.z = z } @@ -611,7 +621,9 @@ class ImplicitDtorDerived : ImplicitDtor { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: [[CAST_BORROWED_PTR:%.*]] = unchecked_ref_cast [[BORROWED_PTR]] : $Builtin.NativeObject to $ImplicitDtorDerived // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[CAST_BORROWED_PTR]] : {{.*}}, #ImplicitDtorDerived.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // CHECK: end_borrow [[BORROWED_PTR]] // -- epilog // CHECK-NOT: unchecked_ref_cast diff --git a/test/SILGen/nested_types_referencing_nested_functions.swift b/test/SILGen/nested_types_referencing_nested_functions.swift index 0e27e309a5dea..99ae2cfcbd571 100644 --- a/test/SILGen/nested_types_referencing_nested_functions.swift +++ b/test/SILGen/nested_types_referencing_nested_functions.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -module-name nested_types_referencing_nested_functions %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -verify -module-name nested_types_referencing_nested_functions %s | %FileCheck %s do { func foo() { bar(2) } @@ -32,3 +32,19 @@ do { _ = x.zim _ = x.zang as (Int) -> () } + +// Invalid case +do { + var x = 123 // expected-note {{captured value declared here}} + + func local() { + // expected-error@-1 {{closure captures 'x' before it is declared}} + _ = x // expected-note {{captured here}} + } + + class Bar { + func zang() { + local() + } + } +} diff --git a/test/SILGen/objc_dealloc.swift b/test/SILGen/objc_dealloc.swift index d9a601e1d5ce5..8c45c5dc68756 100644 --- a/test/SILGen/objc_dealloc.swift +++ b/test/SILGen/objc_dealloc.swift @@ -82,7 +82,9 @@ class SwiftGizmo : Gizmo { // CHECK-NEXT: debug_value [[SELF]] : $SwiftGizmo, let, name "self" // CHECK-NEXT: [[SELF_BORROW:%.*]] = begin_borrow [[SELF]] // CHECK-NEXT: [[X:%[0-9]+]] = ref_element_addr [[SELF_BORROW]] : $SwiftGizmo, #SwiftGizmo.x - // CHECK-NEXT: destroy_addr [[X]] : $*X + // CHECK-NEXT: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X]] + // CHECK-NEXT: destroy_addr [[X_ACCESS]] + // CHECK-NEXT: end_access [[X_ACCESS]] // CHECK-NEXT: end_borrow [[SELF_BORROW]] // CHECK-NEXT: [[RESULT:%[0-9]+]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 6c4e4aba474c3..0d000477cc559 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -616,7 +616,9 @@ func genericProps(_ x: GenericClass) { // CHECK: apply {{.*}}([[ARG]]) : $@convention(method) <τ_0_0> (@guaranteed GenericClass<τ_0_0>) -> Int let _ = x.y // CHECK: [[Z:%.*]] = ref_element_addr [[ARG]] : $GenericClass, #GenericClass.z - // CHECK: [[LOADED_Z:%.*]] = load [copy] [[Z]] : $*String + // CHECK: [[Z_ACCESS:%.*]] = begin_access [read] [unsafe] [[Z]] + // CHECK: [[LOADED_Z:%.*]] = load [copy] [[Z_ACCESS]] : $*String + // CHECK: end_access [[Z_ACCESS]] // CHECK: destroy_value [[LOADED_Z]] // CHECK-NOT: destroy_value [[ARG]] let _ = x.z @@ -626,7 +628,8 @@ func genericProps(_ x: GenericClass) { func genericPropsInGenericContext(_ x: GenericClass) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $GenericClass): // CHECK: [[Z:%.*]] = ref_element_addr [[ARG]] : $GenericClass, #GenericClass.z - // CHECK: copy_addr [[Z]] {{.*}} : $*U + // CHECK: [[Z_ACCESS:%.*]] = begin_access [read] [unsafe] [[Z]] + // CHECK: copy_addr [[Z_ACCESS]] {{.*}} : $*U let _ = x.z } @@ -642,7 +645,9 @@ class ClassWithLetProperty { // CHECK: bb0([[ARG:%.*]] : @guaranteed $ClassWithLetProperty): // CHECK-NEXT: debug_value // CHECK-NEXT: [[PTR:%[0-9]+]] = ref_element_addr [[ARG]] : $ClassWithLetProperty, #ClassWithLetProperty.p -// CHECK-NEXT: [[VAL:%[0-9]+]] = load [trivial] [[PTR]] : $*Int +// CHECK-NEXT: [[PTR_ACCESS:%.*]] = begin_access [read] [unsafe] [[PTR]] +// CHECK-NEXT: [[VAL:%[0-9]+]] = load [trivial] [[PTR_ACCESS]] : $*Int +// CHECK-NEXT: end_access [[PTR_ACCESS]] // CHECK-NEXT: return [[VAL]] : $Int @@ -670,7 +675,8 @@ class r19254812Derived: r19254812Base{ // Initialization of the pi field: no copy_values/releases. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[PB_BOX]] : $*r19254812Derived // CHECK-NEXT: [[PIPTR:%[0-9]+]] = ref_element_addr [[SELF]] : $r19254812Derived, #r19254812Derived.pi -// CHECK-NEXT: assign {{.*}} to [[PIPTR]] : $*Double +// CHECK-NEXT: [[PIPTR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[PIPTR]] +// CHECK-NEXT: assign {{.*}} to [[PIPTR_ACCESS]] : $*Double // CHECK-NOT: destroy_value // CHECK-NOT: copy_value @@ -678,7 +684,8 @@ class r19254812Derived: r19254812Base{ // Load of the pi field: no copy_values/releases. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[PB_BOX]] : $*r19254812Derived // CHECK-NEXT: [[PIPTR:%[0-9]+]] = ref_element_addr [[SELF]] : $r19254812Derived, #r19254812Derived.pi -// CHECK-NEXT: {{.*}} = load [trivial] [[PIPTR]] : $*Double +// CHECK-NEXT: [[PIPTR_ACCESS:%.*]] = begin_access [read] [unsafe] [[PIPTR]] +// CHECK-NEXT: {{.*}} = load [trivial] [[PIPTR_ACCESS]] : $*Double // CHECK: return } diff --git a/test/SILGen/property_wrapper_coroutine.swift b/test/SILGen/property_wrapper_coroutine.swift index bdb6260493a37..0dd8f7cd479eb 100644 --- a/test/SILGen/property_wrapper_coroutine.swift +++ b/test/SILGen/property_wrapper_coroutine.swift @@ -34,7 +34,7 @@ _ = state1.someValues // >> Check that the _modify coroutine is synthesized properly -// CHECK-LABEL: sil hidden [transparent] [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { // CHECK: bb0([[STATE:%.*]] : $*State): // CHECK: debug_value_addr [[STATE]] : $*State, var, name "self", argno {{.*}} // CHECK: [[BEGIN_ACCESS:%.*]] = begin_access [modify] [unknown] [[STATE]] : $*State diff --git a/test/SILGen/property_wrapper_coroutine_public.swift b/test/SILGen/property_wrapper_coroutine_public.swift new file mode 100644 index 0000000000000..1981063e15b8d --- /dev/null +++ b/test/SILGen/property_wrapper_coroutine_public.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// Makes sure the modify coroutine is not @_transparent, since it references +// private properties. + +public class Store { + @Published public var state: Any + init() {} +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get {} + set {} + } + public static subscript( + _enclosingInstance object: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath>) + -> Value where EnclosingSelf : AnyObject { + get {} + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get {} + } +} + +// CHECK-LABEL: sil [ossa] @$s33property_wrapper_coroutine_public5StoreC5stateypvM : $@yield_once @convention(method) (@guaranteed Store) -> @yields @inout Any { +// CHECK: keypath $ReferenceWritableKeyPath, (root $Store; settable_property $Any, id #Store.state!getter.1 : (Store) -> () -> Any, getter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTK : $@convention(thin) (@in_guaranteed Store) -> @out Any, setter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTk : $@convention(thin) (@in_guaranteed Any, @in_guaranteed Store) -> ()) +// CHECK: keypath $ReferenceWritableKeyPath>, (root $Store; stored_property #Store._state : $Published) \ No newline at end of file diff --git a/test/SILGen/protocol_operators.swift b/test/SILGen/protocol_operators.swift new file mode 100644 index 0000000000000..fd11a62cecf8f --- /dev/null +++ b/test/SILGen/protocol_operators.swift @@ -0,0 +1,60 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +infix operator +++ + +protocol Twig { + static func +++(lhs: Self, rhs: Self) +} + +struct Branch : Twig { + @_implements(Twig, +++(_:_:)) + static func doIt(_: Branch, _: Branch) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () { +// CHECK: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ : $@convention(method) (Branch, Branch, @thin Branch.Type) -> () +// CHECK: return +func useBranch(_ b: Branch) { + b +++ b +} + +class Stick : Twig { + static func +++(lhs: Stick, rhs: Stick) {} +} + +class Stuck : Stick, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () { +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: witness_method $Stuck, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +// CHECK: return +func useStick(_ a: Stuck, _ b: Stick) { + _ = a +++ b + _ = b +++ b + _ = a +++ 5 +} + +class Twine : Twig { + static func +++(lhs: Twine, rhs: Twine) {} +} + +class Rope : Twine, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () { +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: witness_method $Rope, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +func useRope(_ r: Rope, _ s: Rope) { + _ = r +++ s + _ = s +++ s + _ = r +++ 5 +} diff --git a/test/SILGen/reabstract-tuple.swift b/test/SILGen/reabstract-tuple.swift index f04ff22b83494..faca9a7862ee2 100644 --- a/test/SILGen/reabstract-tuple.swift +++ b/test/SILGen/reabstract-tuple.swift @@ -27,10 +27,12 @@ class Box { // CHECK: [[CALL:%.*]] = apply [[INIT_F]]<(Int, () -> ())>(%{{.*}}, %{{.*}}) : $@convention(method) <τ_0_0> (@in τ_0_0, @thick Box<τ_0_0>.Type) -> @owned Box<τ_0_0> // CHECK: [[BORROW_CALL:%.*]] = begin_borrow [[CALL]] : $Box<(Int, () -> ())> // CHECK: [[REF:%.*]] = ref_element_addr [[BORROW_CALL]] : $Box<(Int, () -> ())>, #Box.value -// CHECK: [[TUPLEC:%.*]] = load [copy] [[REF]] : $*(Int, @callee_guaranteed () -> @out ()) +// CHECK: [[REF_ACCESS:%.*]] = begin_access [read] [unsafe] [[REF]] +// CHECK: [[TUPLEC:%.*]] = load [copy] [[REF_ACCESS]] : $*(Int, @callee_guaranteed () -> @out ()) // CHECK: ([[TUPLEC_0:%.*]], [[TUPLEC_1:%.*]]) = destructure_tuple [[TUPLEC]] // CHECK: [[THUNK2:%.*]] = function_ref @$sytIegr_Ieg_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @out ()) -> () // CHECK: [[PA2:%.*]] = partial_apply [callee_guaranteed] [[THUNK2]]([[TUPLEC_1]]) : $@convention(thin) (@guaranteed @callee_guaranteed () -> @out ()) -> () +// CHECK: end_access [[REF_ACCESS]] // CHECK: destroy_value [[PA2]] : $@callee_guaranteed () -> () // CHECK: end_borrow [[BORROW_CALL]] : $Box<(Int, () -> ())> // CHECK-LABEL: } // end sil function '$s4main7testBoxyyF' diff --git a/test/SILGen/statements.swift b/test/SILGen/statements.swift index 3015db1a18a24..f33d40ff8fe18 100644 --- a/test/SILGen/statements.swift +++ b/test/SILGen/statements.swift @@ -167,8 +167,8 @@ func for_loops2() { // rdar://problem/19316670 // CHECK: alloc_stack $Optional // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] - // CHECK: [[NEXT:%[0-9]+]] = function_ref @$ss16IndexingIteratorV4next{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[NEXT]]> + // CHECK: [[NEXT:%[0-9]+]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> + // CHECK-NEXT: apply [[NEXT]]>> // CHECK: class_method [[OBJ:%[0-9]+]] : $MyClass, #MyClass.foo!1 let objects = [MyClass(), MyClass() ] for obj in objects { diff --git a/test/SILGen/super_init_refcounting.swift b/test/SILGen/super_init_refcounting.swift index 3872ffea7cfca..ac405f68e01ff 100644 --- a/test/SILGen/super_init_refcounting.swift +++ b/test/SILGen/super_init_refcounting.swift @@ -78,13 +78,17 @@ class Good: Foo { // CHECK: store %0 to [init] [[PB_SELF_BOX]] // CHECK: [[SELF_OBJ:%.*]] = load_borrow [[PB_SELF_BOX]] // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[SELF_OBJ]] : $Good, #Good.x - // CHECK: assign {{.*}} to [[X_ADDR]] : $*Int + // CHECK: [[X_ADDR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[X_ADDR]] + // CHECK: assign {{.*}} to [[X_ADDR_ACCESS]] : $*Int + // CHECK: end_access [[X_ADDR_ACCESS]] // CHECK: [[SELF_OBJ:%.*]] = load [take] [[PB_SELF_BOX]] : $*Good // CHECK: [[SUPER_OBJ:%.*]] = upcast [[SELF_OBJ]] : $Good to $Foo // CHECK: [[BORROWED_SUPER:%.*]] = begin_borrow [[SUPER_OBJ]] // CHECK: [[DOWNCAST_BORROWED_SUPER:%.*]] = unchecked_ref_cast [[BORROWED_SUPER]] : $Foo to $Good // CHECK: [[X_ADDR:%.*]] = ref_element_addr [[DOWNCAST_BORROWED_SUPER]] : $Good, #Good.x - // CHECK: [[X:%.*]] = load [trivial] [[X_ADDR]] : $*Int + // CHECK: [[X_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[X_ADDR]] + // CHECK: [[X:%.*]] = load [trivial] [[X_ADDR_ACCESS]] : $*Int + // CHECK: end_access [[X_ADDR_ACCESS]] // CHECK: end_borrow [[BORROWED_SUPER]] // CHECK: [[SUPER_INIT:%.*]] = function_ref @$s22super_init_refcounting3FooCyACSicfc : $@convention(method) (Int, @owned Foo) -> @owned Foo // CHECK: apply [[SUPER_INIT]]([[X]], [[SUPER_OBJ]]) diff --git a/test/SILGen/top_level_captures.swift b/test/SILGen/top_level_captures.swift new file mode 100644 index 0000000000000..b8ae627ba08cc --- /dev/null +++ b/test/SILGen/top_level_captures.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +guard let x: Int = nil else { while true { } } + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () { +func capturesX() { + _ = x +} + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures17transitiveCaptureyyF : $@convention(thin) (Int) -> () { +// CHECK: [[FUNC:%.*]] = function_ref @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () +func transitiveCapture() { + capturesX() +} diff --git a/test/SILGen/unowned.swift b/test/SILGen/unowned.swift index ebffa09cef626..cfaf7b396bfc7 100644 --- a/test/SILGen/unowned.swift +++ b/test/SILGen/unowned.swift @@ -138,9 +138,10 @@ class TestUnownedMember { // CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[BORROWED_ARG1]] // CHECK: [[FIELDPTR:%.*]] = ref_element_addr [[BORROWED_SELF]] : $TestUnownedMember, #TestUnownedMember.member +// CHECK: [[FIELDPTR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[FIELDPTR]] // CHECK: [[INVAL:%.*]] = ref_to_unowned [[ARG1_COPY]] : $C to $@sil_unowned C // CHECK: [[INVAL_COPY:%.*]] = copy_value [[INVAL]] : $@sil_unowned C -// CHECK: assign [[INVAL_COPY]] to [[FIELDPTR]] : $*@sil_unowned C +// CHECK: assign [[INVAL_COPY]] to [[FIELDPTR_ACCESS]] : $*@sil_unowned C // CHECK: destroy_value [[ARG1_COPY]] : $C // CHECK: end_borrow [[BORROWED_ARG1]] // CHECK: end_borrow [[BORROWED_SELF]] diff --git a/test/SILGen/vtable_generic_signature.swift b/test/SILGen/vtable_generic_signature.swift new file mode 100644 index 0000000000000..78674b0cde823 --- /dev/null +++ b/test/SILGen/vtable_generic_signature.swift @@ -0,0 +1,86 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %target-swift-emit-ir %s + +protocol P {} +protocol Q : P {} + +class ConcreteBase { + func f(_: U) {} +} + +class ConcreteDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericBase { + func f(_: U) {} +} + +class ConcreteDerivedFromGenericBase : GenericBase { + override func f(_: U) {} +} + +class GenericDerivedFromGenericBase : GenericBase<(T) -> Int> { + override func f(_: U) {} +} + +// Make sure we call these methods with the correct substitution map. +func call(_ t: T, _ u: U) { + ConcreteDerivedFromConcreteBase().f(u) + GenericDerivedFromConcreteBase().f(u) + + ConcreteDerivedFromGenericBase().f(u) + GenericDerivedFromGenericBase().f(u) +} + +// All the vtable thunks should traffic in , because that's +// what the base method declares. + +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromConcreteBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromConcreteBase<τ_0_0>) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromGenericBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromGenericBase<τ_0_0>) -> () + +// CHECK-LABEL: sil_vtable ConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature12ConcreteBaseC1fyyxAA1QRzlF +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature12ConcreteBaseCACycfC +// CHECK-NEXT: #ConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature12ConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.f!1: (ConcreteDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromConcreteBase.f!1: (GenericDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature11GenericBaseC1fyyqd__AA1QRd__lF +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature11GenericBaseCACyxGycfC +// CHECK-NEXT: #GenericBase.deinit!deallocator.1: @$s24vtable_generic_signature11GenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.f!1: (ConcreteDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature018GenericDerivedFromD4BaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromGenericBase.f!1: (GenericDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature018GenericDerivedFromD4BaseCfD +// CHECK-NEXT: } diff --git a/test/SILGen/vtable_thunks_reabstraction_modify.swift b/test/SILGen/vtable_thunks_reabstraction_modify.swift index d8c6409482471..7ad5fe25af81a 100644 --- a/test/SILGen/vtable_thunks_reabstraction_modify.swift +++ b/test/SILGen/vtable_thunks_reabstraction_modify.swift @@ -15,21 +15,21 @@ public class DerivedClass : BaseClass { } } -// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) (@guaranteed DerivedClass) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out Result { +// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 { // CHECK: [[DERIVED:%.*]] = function_ref @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvM : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]](%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result +// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]]<τ_0_0>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegyr_SixIegnr_lTR : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result, resume bb1, unwind bb2 +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0, resume bb1, unwind bb2 // CHECK: bb1: -// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegnr_SixIegyr_lTR : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result -// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: end_apply [[TOKEN]] // CHECK: return diff --git a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift index e5cc838e2daec..36c524e27055e 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift @@ -30,17 +30,15 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} } - // FIXME: the following test should produce diagnostics and is a not - // valid uses of the log APIs. The string interpolation passed to the os log - // call must be apart of the log call, it cannot be constructed earlier. - // It nonetheless works fine, but should be rejected. func testNoninlinedOSLogMessage(h: Logger) { let logMessage: OSLogMessage = "Minimum integer value: \(Int.min)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} h.log(level: .debug, logMessage) } func testNoninlinedOSLogMessageComplex(h: Logger, b: Bool) { let logMessage: OSLogMessage = "Maximum integer value: \(Int.max)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} if !b { return; } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil index 9fc968d400de9..744e1043f16c9 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.sil +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -57,7 +57,6 @@ bb0: destroy_value %7 : $OSLogMessageStub %13 = tuple () return %13 : $() - // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] @@ -98,8 +97,6 @@ bb0: dealloc_stack %11 : $*String %15 = tuple () return %15 : $() - // Skip the first literal. - // CHECK: {{%.*}} = string_literal utf8 "some long message: %llx" // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "some long message: %llx" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -175,13 +172,12 @@ bb0: destroy_value %9 : $String %12 = tuple () return %12 : $() - // CHECK-NOT: ({{%.*}}) = destructure_struct {{%.*}} : $OSLogInterpolationStub - // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString - // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) - // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) - // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" - // CHECK-DAG: destroy_value [[STRINGCONST]] : $String + // CHECK-DAG [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG destroy_value [[STRINGCONST]] : $String } // Test that the OSLogOptimization pass does not fold instructions that define @@ -215,7 +211,6 @@ bb0: destroy_value %7 : $OSLogMessageStub %15 = tuple () return %15 : $() - // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) // CHECK-DAG: [[BORROW]] = begin_borrow [[COPYVAL:%[0-9]+]] @@ -275,8 +270,6 @@ bb5: // We must have all string literals at the beginning of the borrowed scope, // and destorys of the literals at the end of the borrow scope. - // Skip the first literal which is the original one. - // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "test message: %lld" // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "test message: %lld" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -535,8 +528,7 @@ bb0: destroy_value %2 : $OSLogMessageStringArrayStub %8 = tuple () return %8 : $() - // Skip the first instance of "ab". - // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" + // The first instance of "ab" will be dead code eliminated. // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) @@ -741,7 +733,7 @@ bb0(%0 : @guaranteed $String): destroy_value %6 : $OSLogMessageStringCapture %18 = tuple () return %18 : $() - // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @idString + // The first instance of function_ref @idString will be dead code eliminated. // CHECK: [[ORIGCAPTURE:%[0-9]+]] = copy_value %0 : $String // CHECK: [[NEWCAPTURE:%[0-9]+]] = copy_value [[ORIGCAPTURE]] : $String // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idString @@ -795,10 +787,9 @@ bb0: dealloc_stack %1 : $*Int64 %18 = tuple () return %18 : $() - // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @genericFunction - // CHECK: [[CLOSUREORIG:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREFORIG]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) + // The first instance of function_ref @genericFunction will be dead-code eliminated. // CHECK: [[FUNREF:%[0-9]+]] = function_ref @genericFunction - // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1]], [[CAPTURE2]]) + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure // CHECK: apply [[USE]]([[BORROW]]) @@ -872,3 +863,105 @@ bb0: %8 = tuple () return %8 : $() } + +// The following tests are for checking dead-code elimination performed by the +// OSLogOptimization pass. + +struct OSLogInterpolationDCEStub { + var formatString: String +} + +struct OSLogMessageDCEStub { + var interpolation: OSLogInterpolationDCEStub +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @owned $OSLogInterpolationDCEStub): + %1 = struct $OSLogMessageDCEStub (%0 : $OSLogInterpolationDCEStub) + return %1 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfStructCreation +sil [ossa] @testDCEOfStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = copy_value %6 : $OSLogInterpolationDCEStub + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = begin_borrow %9 : $OSLogMessageDCEStub + end_borrow %10 : $OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @guaranteed $OSLogInterpolationDCEStub): + %2 = copy_value %0 : $OSLogInterpolationDCEStub + %3 = struct $OSLogMessageDCEStub (%2 : $OSLogInterpolationDCEStub) + return %3 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfGuaranteedStructCreation +sil [ossa] @testDCEOfGuaranteedStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = begin_borrow %6 : $OSLogInterpolationDCEStub + %9 = function_ref @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = apply %9(%7) : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %10 : $OSLogMessageDCEStub + end_borrow %7 : $OSLogInterpolationDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +// CHECK-LABEL: @testLifetimeAdjustmentOfDCE +sil [ossa] @testLifetimeAdjustmentOfDCE : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = copy_value %5 : $String + %7 = struct $OSLogInterpolationDCEStub(%5 : $String) + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%6) : $@convention(thin) (@guaranteed String) -> () + destroy_value %6 : $String + %13 = tuple () + return %13 : $() + // CHECK: [[STRINGINITREF:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRING:%[0-9]+]] = apply [[STRINGINITREF]]( + // CHECK-NEXT: [[COPY:%[0-9]+]] = copy_value [[STRING]] + // CHECK-NEXT: destroy_value [[STRING]] + // CHECK-NOT: OSLogInterpolationDCEStub + // CHECK-NOT: OSLogMessageDCEStub + // CHECK: return +} diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 39293a7f1cbe0..a0360b3b4dd8d 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -460,5 +460,34 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } + + // CHECK-LABEL: @$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF + func testDeadCodeElimination( + h: Logger, + number: Int, + num32bit: Int32, + string: String + ) { + h.log("A message with no data") + h.log("smallstring") + h.log( + level: .error, + """ + A message with many interpolations \(number), \(num32bit), \(string), \ + and a suffix + """) + h.log( + level: .info, + """ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(48) \(49) + """) + let concatString = string + ":" + String(number) + h.log("\(concatString)") + // CHECK-NOT: OSLogMessage + // CHECK-LABEL: end sil function '$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF' + } } diff --git a/test/SILOptimizer/access_marker_mandatory.swift b/test/SILOptimizer/access_marker_mandatory.swift index 0346b3a86bd31..97e27d5756542 100644 --- a/test/SILOptimizer/access_marker_mandatory.swift +++ b/test/SILOptimizer/access_marker_mandatory.swift @@ -49,8 +49,6 @@ func takeS(_ s: S) {} // CHECK: [[ADDRI:%.*]] = struct_element_addr [[WRITE]] : $*S, #S.i // CHECK: store %{{.*}} to [[ADDRI]] : $*Int // CHECK: end_access [[WRITE]] -// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*S -// CHECK: end_access [[READ]] // CHECK: [[FTAKE:%.*]] = function_ref @$s23access_marker_mandatory5takeSyyAA1SVF : $@convention(thin) (@guaranteed S) -> () // CHECK: apply [[FTAKE]](%{{.*}}) : $@convention(thin) (@guaranteed S) -> () // CHECK-LABEL: } // end sil function '$s23access_marker_mandatory14modifyAndReadS1oyyXl_tF' diff --git a/test/SILOptimizer/allocbox_to_stack_ownership.sil b/test/SILOptimizer/allocbox_to_stack_ownership.sil index 73f403011570d..12cfada7e7288 100644 --- a/test/SILOptimizer/allocbox_to_stack_ownership.sil +++ b/test/SILOptimizer/allocbox_to_stack_ownership.sil @@ -391,10 +391,10 @@ bb0(%0 : $Int): return %10 : $() // id: %11 } -// CHECK-LABEL: sil shared [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n -// CHECK-LABEL: sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ +// CHECK-LABEL: sil shared [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n +// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ // struct.(useStack (t : Swift.Int) -> ()).(closure #1) -sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { +sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } ): %1 = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 // function_ref Swift.++ @postfix (x : @inout A) -> A diff --git a/test/SILOptimizer/arcsequenceopts.sil b/test/SILOptimizer/arcsequenceopts.sil index 1cc48079154a1..b6e024270a495 100644 --- a/test/SILOptimizer/arcsequenceopts.sil +++ b/test/SILOptimizer/arcsequenceopts.sil @@ -571,10 +571,16 @@ bb0(%0 : $Cls): return %r : $() } -// CHECK-LABEL: sil @dont_remove_as_local_object_indirectly_escapes_to_callee -// CHECK: retain_value -// CHECK: release_value -sil @dont_remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// The original SIL already has an over-release of the parent. +// +// CHECK-LABEL: sil @remove_as_local_object_indirectly_escapes_to_callee +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { bb0(%0 : $Cls): %1 = alloc_ref $Cls %2 = alloc_ref $Container @@ -589,6 +595,30 @@ bb0(%0 : $Cls): return %r : $() } +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// +// CHECK-LABEL: sil @local_object_indirectly_escapes_to_use_and_owned_arg +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @local_object_indirectly_escapes_to_use_and_owned_arg : $@convention(thin) (Cls) -> () { +bb0(%0 : $Cls): + %1 = alloc_ref $Cls + %2 = alloc_ref $Container + %3 = ref_element_addr %2 : $Container, #Container.c + store %1 to %3 : $*Cls + retain_value %1 : $Cls + %f1 = function_ref @use_container : $@convention(thin) (@guaranteed Container) -> () + apply %f1 (%2) : $@convention(thin) (@guaranteed Container) -> () + %f2 = function_ref @release_container : $@convention(thin) (Container) -> () + apply %f2 (%2) : $@convention(thin) (Container) -> () + release_value %1 : $Cls + %r = tuple() + return %r : $() +} + sil @release_arg1 : $@convention(thin) (Cls, Cls) -> () { bb0(%0 : $Cls, %1 : $Cls): strong_release %1 : $Cls @@ -596,6 +626,14 @@ bb0(%0 : $Cls, %1 : $Cls): return %r : $() } +sil @use_container : $@convention(thin) (@guaranteed Container) -> () { +bb0(%0 : $Container): + %1 = ref_element_addr %0 : $Container, #Container.c + %2 = load %1 : $*Cls + %r = tuple() + return %r : $() +} + sil @release_container : $@convention(thin) (Container) -> () { bb0(%0 : $Container): strong_release %0 : $Container diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 7a0dfad4330ea..cb4b73a37a874 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -enforce-exclusivity=unchecked %s | %FileCheck %s +// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // This is an end-to-end test of the array(contentsOf) -> array(Element) optimization @@ -14,18 +14,15 @@ public func testInt(_ a: inout [Int]) { a += [1] } -// CHECK-LABEL: sil @{{.*}}testThreeInt -// CHECK-NOT: apply -// CHECK: [[FR:%[0-9]+]] = function_ref @$sSa15reserveCapacityyySiFSi_Tg5 -// CHECK-NEXT: apply [[FR]] -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: tuple -// CHECK-NEXT: return +// CHECK-LABEL: sil @{{.*}}testThreeInts +// CHECK-DAG: [[FR:%[0-9]+]] = function_ref @${{(sSa15reserveCapacityyySiFSi_Tg5|sSa16_createNewBuffer)}} +// CHECK-DAG: apply [[FR]] +// CHECK-DAG: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK: } // end sil function '{{.*}}testThreeInts{{.*}}' + public func testThreeInts(_ a: inout [Int]) { a += [1, 2, 3] } diff --git a/test/SILOptimizer/closure-lifetime-fixup.sil b/test/SILOptimizer/closure-lifetime-fixup.sil index caf2dcc47136a..6ece4cb4e2ec7 100644 --- a/test/SILOptimizer/closure-lifetime-fixup.sil +++ b/test/SILOptimizer/closure-lifetime-fixup.sil @@ -155,3 +155,26 @@ bb1: %86 = tuple () return %86 : $() } + +sil [ossa] @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool +sil [ossa] @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + +// Don't crash. +// CHECK-LABEL: sil hidden [ossa] @testUndefined +// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] +// CHECK: mark_dependence +// CHECK: mark_dependence +// CHECK: dealloc_stack [[PA]] : $@noescape @callee_guaranteed () -> Bool +// CHECK: destroy_value +sil hidden [ossa] @testUndefined : $@convention(method) (@guaranteed Klass, @guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): + %4 = function_ref @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %5 = copy_value %1 : $Klass + %6 = partial_apply [callee_guaranteed] %4(%5, undef) : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %7 = convert_escape_to_noescape [not_guaranteed] %6 : $@callee_guaranteed () -> Bool to $@noescape @callee_guaranteed () -> Bool + %21 = function_ref @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + %22 = apply %21(%7) : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + destroy_value %6 : $@callee_guaranteed () -> Bool + %42 = tuple () + return %42 : $() +} diff --git a/test/SILOptimizer/closure_lifetime_fixup_undef.swift b/test/SILOptimizer/closure_lifetime_fixup_undef.swift new file mode 100644 index 0000000000000..cf27396999f52 --- /dev/null +++ b/test/SILOptimizer/closure_lifetime_fixup_undef.swift @@ -0,0 +1,13 @@ +// RUN: not %target-swift-frontend %s -sil-verify-all -c 2>&1 | %FileCheck %s + +// Report the error but don't crash. +// CHECK: error: closure captures 'stringList' before it is declared + +class TestUndefined { + private var stringList: [String]! + + func dontCrash(strings: [String]) { + assert(stringList.allSatisfy({ $0 == stringList.first!})) + let stringList = strings.filter({ $0 == "a" }) + } +} diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index 8e1b6fa33b592..12d710f9165ae 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -14,7 +14,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory diff --git a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift index 917074e565744..07e17465aa831 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift @@ -16,7 +16,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index f6b40025e5cb9..c8adf1618c29f 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -42,14 +42,18 @@ class Cat : FakeNSObject { // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $Cat // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x - // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [modify] [unsafe] [[FIELD_ADDR]] + // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked + // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[COND:%.*]] = struct_extract %1 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x - // CHECK-NEXT: destroy_addr [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[FIELD_ADDR]] + // CHECK-NEXT: destroy_addr [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked + // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[RELOAD_FROM_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick Cat.Type diff --git a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil index 5726186e7d536..d2e9a949690bf 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil @@ -89,10 +89,10 @@ bb0(%0 : $*MyStruct2, %1 : $@thin MyStruct2.Type): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() @@ -114,12 +114,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() diff --git a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil index d0be2dca9fded..8ab4fc33ec85b 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil @@ -63,8 +63,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -87,7 +87,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): // Finally perform the epilog. %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -112,8 +112,8 @@ bb0(%0 : @owned $DerivedClassWithIVars): sil [ossa] @derived_test2 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %11 = load [take] %3 : $*DerivedClassWithIVars @@ -124,7 +124,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -195,7 +195,9 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): // Now we destroy the stored value. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[SELFBOX]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[SELF]] // // And then perform dealloc_partial_ref. @@ -246,8 +248,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Int) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Initialize properties in derived class. @@ -274,6 +276,6 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %16 = unchecked_ref_cast %15 : $RootClassWithIVars to $DerivedClassWithIVars store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil index d9346af18cefd..ffc00fc2f1dfe 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil @@ -109,7 +109,9 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[BORROWED_SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[METATYPE:%[0-9]+]] = metatype $@thick RootClassWithNontrivialStoredProperties.Type // CHECK: dealloc_partial_ref [[SELF]] : $RootClassWithNontrivialStoredProperties, [[METATYPE]] : $@thick RootClassWithNontrivialStoredProperties.Type diff --git a/test/SILOptimizer/definite_init_markuninitialized_var.sil b/test/SILOptimizer/definite_init_markuninitialized_var.sil index fc41fed4a68e5..270d6c5b65516 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_var.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_var.sil @@ -15,10 +15,10 @@ sil @makesInt : $@convention(thin) () -> Int sil [ossa] @use_before_init : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %4 = load [trivial] %1 : $*Int // expected-error {{variable '' used before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %4 : $Int } @@ -26,13 +26,13 @@ bb0: sil [ossa] @inout_uninit : $@convention(thin) () -> () { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '' passed by reference before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } %t = tuple () return %t : $() } @@ -48,8 +48,8 @@ bb0: sil [ossa] @used_by_inout : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Int + %91a = mark_uninitialized [var] %91 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %91a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [trivial] %1 : $*Int %3 = load [trivial] %1 : $*Int @@ -57,7 +57,7 @@ bb0(%0 : $Int): %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () %7 = load [trivial] %1 : $*Int %8 = tuple (%3 : $Int, %7 : $Int) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } + destroy_value %91a : $<τ_0_0> { var τ_0_0 } return %8 : $(Int, Int) } @@ -74,14 +74,14 @@ sil [ossa] @returns_generic_struct : $@convention(thin) () -> @out AddressOnlySt sil [ossa] @call_struct_return_function : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Int - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Int } @@ -89,15 +89,14 @@ bb0: sil [ossa] @tuple_elements1 : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %4 = tuple_element_addr %3 : $*(Int, Int), 0 %5 = tuple_element_addr %3 : $*(Int, Int), 1 %14 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %15 = tuple_element_addr %3 : $*(Int, Int), 1 %16 = apply %14(%15) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '.1' passed by reference before being initialized}} - - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> %99 = tuple () return %99 : $() } @@ -106,15 +105,15 @@ bb0(%0 : $Int): sil [ossa] @tuple_elements2 : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %18 = tuple_element_addr %3 : $*(Int, Int), 0 store %0 to [trivial] %18 : $*Int %20 = load [trivial] %3 : $*(Int, Int) // expected-error {{variable '.1' used before being initialized}} %21 = tuple_extract %20 : $(Int, Int), 0 %22 = tuple_extract %20 : $(Int, Int), 1 %23 = tuple (%21 : $Int, %22 : $Int) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> return %23 : $(Int, Int) } @@ -122,11 +121,11 @@ bb0(%0 : $Int): sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -135,10 +134,10 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @copy_addr2 : $@convention(thin) (@in_guaranteed T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T // expected-note {{variable defined here}} + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '' used before being initialized}} - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -150,16 +149,16 @@ sil [ossa] @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) sil [ossa] @closure_test : $@convention(thin) () -> () { bb0: %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %0 = mark_uninitialized [var] %1a : $*Int // expected-note {{variable defined here}} + %1a = mark_uninitialized [var] %1 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %0 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_closure : $@convention(thin) (@owned @callee_owned () -> ()) -> () %6 = function_ref @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () - %1copy = copy_value %1 : $<τ_0_0> { var τ_0_0 } + %1copy = copy_value %1a : $<τ_0_0> { var τ_0_0 } mark_function_escape %0 : $*Int // expected-error {{variable '' used by function definition before being initialized}} %8 = partial_apply %6(%1copy) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () %9 = apply %5(%8) : $@convention(thin) (@owned @callee_owned () -> ()) -> () - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -176,8 +175,8 @@ sil [ossa] @getSomeOptionalClass : $@convention(thin) () -> @owned Optional Int { bb0(%0 : $Int): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Int + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -186,7 +185,7 @@ bb0(%0 : $Int): assign %0 to %1 : $*Int %2 = load [trivial] %1 : $*Int - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Int } @@ -198,8 +197,8 @@ bb0: // lone store), the second becomes a reassignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -217,7 +216,7 @@ bb0: destroy_addr %c : $*SomeClass // CHECK-NEXT: destroy_addr - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -227,8 +226,8 @@ bb0: sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 // CHECK: [[PB:%[0-9]+]] = project_box @@ -244,7 +243,7 @@ bb0(%0 : $*T, %1 : $*T): copy_addr %2 to [initialization] %0 : $*T // CHECK-NEXT: copy_addr [[PB]] to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } // CHECK-NEXT: destroy_value %2 %9 = tuple () return %9 : $() @@ -257,8 +256,8 @@ bb0: // second becomes an assignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_weak Optional + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 %f = function_ref @getSomeOptionalClass : $@convention(thin) () -> @owned Optional @@ -281,7 +280,7 @@ bb0: destroy_value %8 : $Optional // CHECK-NEXT: destroy_value [[C2]] - destroy_value %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + destroy_value %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> %11 = tuple () return %11 : $() @@ -294,8 +293,8 @@ bb0: // second becomes a reassignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -328,7 +327,7 @@ bb0: // CHECK-NEXT: destroy_value [[C2]] destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() @@ -342,12 +341,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -557,8 +556,8 @@ bb2: sil [ossa] @conditionalInitOrAssignAllocBox : $@convention(thin) () -> () { bb0: %box = alloc_box $<τ_0_0> { var τ_0_0 } - %5 = project_box %box : $<τ_0_0> { var τ_0_0 } , 0 - %7 = mark_uninitialized [var] %5 : $*SomeClass + %5 = mark_uninitialized [var] %box : $<τ_0_0> { var τ_0_0 } + %7 = project_box %5 : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass cond_br undef, bb1, bb2 @@ -566,11 +565,11 @@ bb0: bb1: %12 = apply %2() : $@convention(thin) () -> @owned SomeClass assign %12 to %7 : $*SomeClass - destroy_value %box : $<τ_0_0> { var τ_0_0 } + destroy_value %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb2: - destroy_value %box : $<τ_0_0> { var τ_0_0 } + dealloc_box %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb3: diff --git a/test/SILOptimizer/definite_init_scalarization_test.sil b/test/SILOptimizer/definite_init_scalarization_test.sil index 6f45b22dc4c58..a2f4a88d2367d 100644 --- a/test/SILOptimizer/definite_init_scalarization_test.sil +++ b/test/SILOptimizer/definite_init_scalarization_test.sil @@ -43,12 +43,12 @@ struct ObjectValue { sil [ossa] @test_store_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) store %6 to [trivial] %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -73,12 +73,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_store_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) store %6 to [init] %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -103,12 +103,12 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assign_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -139,14 +139,14 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_trivial_2 : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) %7 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) assign %7 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -171,12 +171,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -209,14 +209,14 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assigned_owned_2 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) %7 = copy_value %6 : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %7 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } diff --git a/test/SILOptimizer/devirt_speculate.swift b/test/SILOptimizer/devirt_speculate.swift index f9a75f4f12de2..e45f573d1147a 100644 --- a/test/SILOptimizer/devirt_speculate.swift +++ b/test/SILOptimizer/devirt_speculate.swift @@ -10,44 +10,96 @@ public class Base { public init() {} public func foo() {} } + +@_optimize(none) +func blackHole(_: T) {} + class Sub1 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub2 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub3 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub4 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub5 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub6 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub7 : Base { - override func foo() {} + override func foo() { blackHole(self) } } // CHECK: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// CHECK: checked_cast_br [exact] %0 : $Base to Base -// CHECK: checked_cast_br [exact] %0 : $Base to Sub1 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub2 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub3 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub4 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub5 -// CHECK: checked_cast_br [exact] %0 : $Base to Sub6 +// CHECK: bb0(%0 : $Base): +// CHECK: checked_cast_br [exact] %0 : $Base to Base, bb2, bb3 + +// CHECK: bb2([[CASTED:%.*]]): +// CHECK: br bb1 + +// CHECK: bb3: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub1, bb4, bb5 + +// CHECK: bb4([[CASTED:%.*]] : $Sub1): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb5: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub2, bb6, bb7 + +// CHECK: bb6([[CASTED:%.*]] : $Sub2): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb7: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub3, bb8, bb9 + +// CHECK: bb8([[CASTED:%.*]] : $Sub3): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb9: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub4, bb10, bb11 + +// CHECK: bb10([[CASTED:%.*]] : $Sub4): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb11: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub5, bb12, bb13 + +// CHECK: bb12([[CASTED:%.*]] : $Sub5): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb13: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub6, bb14, bb15 + +// CHECK: bb14([[CASTED:%.*]] : $Sub6): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb15: // CHECK-NOT: checked_cast_br -// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () +// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () // YAML: Pass: sil-speculative-devirtualizer // YAML-NEXT: Name: sil.PartialSpecDevirt // YAML-NEXT: DebugLoc: // YAML-NEXT: File: {{.*}}/devirt_speculate.swift -// YAML-NEXT: Line: 66 +// YAML-NEXT: Line: 118 // YAML-NEXT: Column: 5 // YAML-NEXT: Function: 'testMaxNumSpeculativeTargets(_:)' // YAML-NEXT: Args: diff --git a/test/SILOptimizer/devirt_speculative.sil b/test/SILOptimizer/devirt_speculative.sil index 77ae33ce71d96..cac316601dc3c 100644 --- a/test/SILOptimizer/devirt_speculative.sil +++ b/test/SILOptimizer/devirt_speculative.sil @@ -145,7 +145,6 @@ bb0(%0: $Base2): // CHECK-NEXT: unreachable // CHECK: checked_cast_br // CHECK: function_ref @Sub_exit -// CHECK-NEXT: unchecked_ref_cast // CHECK-NEXT: apply // CHECK-NEXT: unreachable sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { diff --git a/test/SILOptimizer/di-conditional-destroy-scope.swift b/test/SILOptimizer/di-conditional-destroy-scope.swift index 366c0d39399da..e87d8a7da9ed0 100644 --- a/test/SILOptimizer/di-conditional-destroy-scope.swift +++ b/test/SILOptimizer/di-conditional-destroy-scope.swift @@ -6,8 +6,9 @@ // REQUIRES: objc_interop -// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:38:5, scope 2 -// CHECK: destroy_addr [[ADR]] : $*FileSystem, loc {{.*}}:38:5, scope 2 +// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:39:5, scope 2 +// CHECK: [[ADR_ACCESS:%.*]] = begin_access [deinit] [static] [[ADR]] +// CHECK: destroy_addr [[ADR_ACCESS]] : $*FileSystem, loc {{.*}}:39:5, scope 2 import Foundation diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 25085c9609fbc..e137f3ab5515c 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -77,8 +77,8 @@ struct FourFields { sil @take_indirect_tuple : $@convention(method) (@in (Int, ())) -> () // CHECK-LABEL: CG of handle_undef -// CHECK: Val %2 Esc: G, Succ: (%2.1) -// CHECK: Con %2.1 Esc: G, Succ: +// CHECK: Val %2 Esc: , Succ: (%2.1) +// CHECK: Con [ref] %2.1 Esc: G, Succ: // CHECK: End sil @handle_undef : $@convention(thin) (Int) -> () { bb0(%0 : $Int): @@ -96,11 +96,12 @@ bb0(%0 : $Int): // CHECK-LABEL: CG of test_simple // CHECK-NEXT: Arg %0 Esc: A, Succ: (%5) -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %1 -// CHECK-NEXT: Con %5 Esc: A, Succ: %2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -117,12 +118,12 @@ bb0(%0 : $*Y, %1 : $X): // Test if a deferring edge is created for a block argument. // CHECK-LABEL: CG of deferringEdge -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: %3, Succ: ([rc] %4), %0 -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: %1 -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: A, Succ: %1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode, %1 : $LinkedNode): @@ -137,8 +138,10 @@ bb1(%3 : $LinkedNode): // Test a local object just escaping via return. // CHECK-LABEL: CG of escapes_via_return -// CHECK-NEXT: Val %0 Esc: R, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: R, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: R, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @escapes_via_return : $@convention(thin) () -> @owned X { bb0: @@ -149,14 +152,14 @@ bb0: // A linear chain of assignments is collapsed to a single node. // CHECK-LABEL: CG of test_linked_list -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%13) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %2) -// CHECK-NEXT: Val %11 Esc: %11, Succ: ([rc] %2), %7, %13 -// CHECK-NEXT: Con %13 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %13 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 +// CHECK-NEXT: Con [ref] %13 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -184,14 +187,14 @@ bb2: // The same example as above but distributed over two functions. // CHECK-LABEL: CG of create_chain -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %1 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %4 Esc: A, Succ: ([rc] %8) -// CHECK-NEXT: Val %7 Esc: %11, Succ: ([rc] %8) -// CHECK-NEXT: Con [rc] %8 Esc: A, Succ: (%8.1) -// CHECK-NEXT: Con %8.1 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Val %11 Esc: R, Succ: ([rc] %8), %8.1 -// CHECK-NEXT: Ret return Esc: R, Succ: %11 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) +// CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Con [ref] %8.1 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -211,11 +214,11 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of loadNext -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %2 Esc: %2, Succ: ([rc] %3), %0, %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%3) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -233,15 +236,17 @@ bb2: // Content nodes in the callee are duplicated in the caller. // CHECK-LABEL: CG of call_load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %0.3) -// CHECK-NEXT: Con [rc] %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: ([rc] %0.5) -// CHECK-NEXT: Con [rc] %0.5 Esc: A, Succ: (%0.6) -// CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6 -// CHECK-NEXT: Ret return Esc: R, Succ: %2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) +// CHECK-NEXT: Con [ref] %0.4 Esc: A, Succ: (%0.5) +// CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) +// CHECK-NEXT: Con [ref] %0.6 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -251,14 +256,16 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %4 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: +// CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -277,10 +284,12 @@ sil_global @global_x : $X // The argument escapes because it is stored to a global variable in the callee. // CHECK-LABEL: CG of call_store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %5) -// CHECK-NEXT: Con [rc] %5 Esc: G, Succ: (%6) +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: G, Succ: (%6) // CHECK-NEXT: Con %6 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Con [int] %6.1 Esc: G, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -294,9 +303,9 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_pointer : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -310,10 +319,10 @@ bb0(%0 : $Pointer): // global variable in the callee. // CHECK-LABEL: CG of store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %4 -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) // CHECK-NEXT: Con %4 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { @@ -328,10 +337,12 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of call_store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%5) // CHECK-NEXT: Con %5 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -345,9 +356,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of copy_addr_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -358,9 +369,9 @@ bb0(%0: $*Int32, %1: $*Int32): // CHECK-LABEL: CG of copy_addr_take_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_take_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -370,10 +381,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of copy_addr_noinit_content -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: // CHECK-NEXT: End sil @copy_addr_noinit_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -383,10 +394,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of call_copy_addr_content -// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1 -// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %3, Succ: +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: %3, Succ: %1.1 +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: %3, Succ: // CHECK-NEXT: End sil @call_copy_addr_content : $@convention(thin) () -> () { %0 = alloc_stack $Int32 @@ -404,14 +415,17 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // of Y's box _could_ capture Y.x in Y's deinit. // CHECK-LABEL: CG of test_partial_apply -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: ([rc] %7) -// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: ([rc] %7) -// CHECK-NEXT: Con [rc] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%1.1), %1 +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%7) +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: (%7) +// CHECK-NEXT: Con [int] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) // CHECK-NEXT: Con %7.1 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { bb0(%0 : $Int64, %1 : $X, %2 : $Y): @@ -434,16 +448,21 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y): } // CHECK-LABEL: CG of closure1 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %3) -// CHECK-NEXT: Arg %2 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) // CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: -// CHECK-NEXT: Con [rc] %4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: A, Succ: ([rc] %4.2) -// CHECK-NEXT: Con [rc] %4.2 Esc: G, Succ: -// CHECK-NEXT: Val %7 Esc: %8, Succ: %2 +// CHECK-NEXT: Con [int] %3.2 Esc: A, Succ: (%3.3) +// CHECK-NEXT: Con %3.3 Esc: A, Succ: (%3.4) +// CHECK-NEXT: Con %3.4 Esc: G, Succ: +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [int] %4.2 Esc: A, Succ: +// CHECK-NEXT: Con %4.3 Esc: A, Succ: (%0.1), %0 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %2 // CHECK-NEXT: End sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } , @owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } ): @@ -458,12 +477,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } // CHECK-LABEL: CG of closure2 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %4) -// CHECK-NEXT: Con [rc] %4 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -479,11 +499,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): // Test partial_apply. The box escapes in the callee. // CHECK-LABEL: CG of test_escaped_box -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Con [int] %2.2 Esc: G, Succ: +// CHECK-NEXT: Con %2.3 Esc: G, Succ: +// CHECK-NEXT: Con %2.4 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 { bb0(%0 : $Int64): @@ -502,10 +524,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of let_box_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: G, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: G, Succ: (%1.3) +// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -524,9 +548,10 @@ sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> ( // The partial_apply itself escapes and therefore also the box escapes. // CHECK-LABEL: CG of test_escaped_partial_apply -// CHECK-NEXT: Val %1 Esc: G, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () { bb0(%0 : $Int64): @@ -544,10 +569,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of closure3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Con [int] %1.2 Esc: A, Succ: +// CHECK-NEXT: Con %1.3 Esc: A, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -564,11 +591,13 @@ sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: G, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2 +// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) +// CHECK-NEXT: Con [ref] %4.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -580,12 +609,13 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 -// CHECK-NEXT: Val %4 Esc: G, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -597,12 +627,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of return_same -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %3 Esc: G, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: G, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: G, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %5 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: (%5.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -620,15 +650,15 @@ bb2(%5 : $LinkedNode): // Another recursion test. // CHECK-LABEL: CG of loadNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %2.1) -// CHECK-NEXT: Con [rc] %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Con [rc] %2.3 Esc: A, Succ: (%2.4) -// CHECK-NEXT: Con %2.4 Esc: A, Succ: ([rc] %2.3) -// CHECK-NEXT: Val %4 Esc: R, Succ: %2.2, %2.4 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 +// CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [ref] %4.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -640,14 +670,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of returnNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %5) -// CHECK-NEXT: Val %3 Esc: R, Succ: ([rc] %3.1), %3.2 -// CHECK-NEXT: Con [rc] %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Con [rc] %5 Esc: A, Succ: (%6) -// CHECK-NEXT: Con %6 Esc: A, Succ: ([rc] %3.1) -// CHECK-NEXT: Val %8 Esc: R, Succ: %3, %6 -// CHECK-NEXT: Ret return Esc: R, Succ: %8 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 +// CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) +// CHECK-NEXT: Con [ref] %8.2 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -670,12 +700,12 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Val %5 Esc: R, Succ: ([rc] %2), %3 -// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -695,9 +725,11 @@ bb2(%5 : $LinkedNode): // Test if a try_apply is represented correctly in the connection graph. // CHECK-LABEL: CG of call_throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: R, Succ: %0 -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -712,10 +744,12 @@ bb2(%5 : $Error): } // CHECK-LABEL: CG of throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -735,11 +769,13 @@ bb2: // Test if a try_apply to an unknown function is handled correctly. // CHECK-LABEL: CG of call_unknown_throwing_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -758,12 +794,14 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: %6, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: %6, Succ: (%2.1) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: %6, Succ: (%2.1) // CHECK-NEXT: Con %2.1 Esc: %6, Succ: %0 -// CHECK-NEXT: Val %5 Esc: %6, Succ: %1 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { bb0(%0 : $Y): @@ -782,10 +820,10 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // Test is an unknown value is merged correctly into the caller graph. // CHECK-LABEL: CG of store_to_unknown_reference -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -798,9 +836,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of get_reference -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: + // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { bb0: @@ -816,9 +855,11 @@ sil @unknown_get_reference : $@convention(thin) () -> @owned Y sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-LABEL: CG of get_y -// CHECK-NEXT: Val %0 Esc: G, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %3 +// CHECK-NEXT: Val %0 Esc: , Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: G, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { bb0: @@ -831,10 +872,10 @@ bb0: } // CHECK-LABEL: CG of create_and_store_x -// CHECK-NEXT: Val %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: ([rc] %3) -// CHECK-NEXT: Con [rc] %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { bb0: @@ -850,11 +891,13 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %0, %1 -// CHECK-NEXT: Val %7 Esc: R, Succ: %4 -// CHECK-NEXT: Ret return Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%7.1), %4 +// CHECK-NEXT: Con [int] %7.1 Esc: A, Succ: (%7.2) +// CHECK-NEXT: Con %7.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { bb0(%0 : $Y, %1 : $Y): @@ -873,10 +916,10 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-LABEL: CG of defer_edge_cycle // CHECK-NEXT: Arg %0 Esc: A, Succ: (%2) // CHECK-NEXT: Arg %1 Esc: A, Succ: (%4) -// CHECK-NEXT: Con %2 Esc: A, Succ: ([rc] %6), %4 -// CHECK-NEXT: Con %4 Esc: A, Succ: %2 -// CHECK-NEXT: Con [rc] %6 Esc: A, Succ: (%7) -// CHECK-NEXT: Con %7 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) +// CHECK-NEXT: Con [ref] %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { entry(%0 : $*Y, %1 : $*Y): @@ -891,9 +934,10 @@ entry(%0 : $*Y, %1 : $*Y): } // CHECK-LABEL: CG of take_c_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () { bb0(%0 : $@convention(c) () -> ()): @@ -910,8 +954,9 @@ bb0: } // CHECK-LABEL: CG of pass_c_func -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: +// CHECK-NEXT: Con %2.2 Esc: G, Succ: // CHECK-NEXT: End sil @pass_c_func : $@convention(thin) () -> () { bb0: @@ -924,12 +969,14 @@ bb0: // CHECK-LABEL: CG of test_select_enum -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -939,11 +986,13 @@ bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_select_enum_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -952,11 +1001,13 @@ bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_select_value -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret return Esc: R, Succ: %6 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): @@ -967,10 +1018,10 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_existential_addr -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%2) // CHECK-NEXT: Con %2 Esc: , Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: , Succ: %0 +// CHECK-NEXT: Con [ref] %2.1 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -985,7 +1036,7 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of test_existential_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @test_existential_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1000,9 +1051,9 @@ bb0(%0 : $X): // Check that we don't crash on this. // CHECK-LABEL: CG of test_unknown_store -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () { bb0(%0 : $ErrorClass): @@ -1014,8 +1065,10 @@ bb0(%0 : $ErrorClass): } // CHECK-LABEL: CG of test_raw_pointer_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X { bb0(%0 : $X): @@ -1025,8 +1078,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of test_bridge_object_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X { bb0(%0 : $X, %1 : $Builtin.Word): @@ -1037,8 +1092,8 @@ bb0(%0 : $X, %1 : $Builtin.Word): // CHECK-LABEL: CG of test_address_to_pointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1 -// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: // CHECK-NEXT: End sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X { bb0(%0 : $*X, %1 : $X): @@ -1050,8 +1105,10 @@ bb0(%0 : $*X, %1 : $X): } // CHECK-LABEL: CG of test_casts -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret return Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X { bb0(%0 : $AnyObject): @@ -1074,10 +1131,10 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) // CHECK-NEXT: Val %1 Esc: G, Succ: (%8) // CHECK-NEXT: Val %4 Esc: , Succ: (%8) -// CHECK-NEXT: Con %8 Esc: G, Succ: (%8), %1 +// CHECK-NEXT: Con [ref] %8 Esc: G, Succ: (%8), %1 // CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %8 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { @@ -1104,7 +1161,9 @@ bb0(%0 : $Y): } // CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1116,7 +1175,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_subscript -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_subscript : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1133,7 +1194,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_index -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_index : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1149,9 +1212,9 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_get_element // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2 -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %1.1) -// CHECK-NEXT: Con [rc] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.2 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) // CHECK-NEXT: Con %1.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_element : $@convention(thin) (Array) -> @out X { @@ -1170,6 +1233,7 @@ bb0(%io : $*X, %1 : $Array): // CHECK-LABEL: CG of arraysemantics_make_mutable // CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array) -> () { bb0(%0 : $*Array): @@ -1181,9 +1245,10 @@ bb0(%0 : $*Array): } // CHECK-LABEL: CG of arraysemantics_get_element_address -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: [rc] %0.1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: %0.1 // CHECK-NEXT: End sil @arraysemantics_get_element_address : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1198,7 +1263,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_count -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_count : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1211,7 +1278,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_capacity -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_capacity : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1225,12 +1294,14 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: ([rc] %0.2) -// CHECK-NEXT: Con [rc] %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [int] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [ref] %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array, @owned @callee_owned (@inout X) -> (@out (), @error Error)) -> () { bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): @@ -1245,12 +1316,12 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): } // CHECK-LABEL: CG of arraysemantics_createUninitialized -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: R, Succ: %0 -// CHECK-NEXT: Val %4 Esc: R, Succ: ([rc] %6) -// CHECK-NEXT: Con [rc] %6 Esc: R, Succ: %2 -// CHECK-NEXT: Ret return Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%6) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: R, Succ: (%6.1) +// CHECK-NEXT: Con %6.1 Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { bb0(%0 : $X): @@ -1284,11 +1355,17 @@ sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(metho sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // CHECK-LABEL: CG of semantics_pair_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: %5, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: %5, Succ: // CHECK-NEXT: End sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1301,10 +1378,14 @@ bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of semantics_self_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1317,7 +1398,7 @@ bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of check_dealloc_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1327,7 +1408,7 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of check_set_deallocating -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_set_deallocating : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1340,7 +1421,9 @@ bb0(%0 : $X): // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1355,7 +1438,9 @@ bb0: // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1370,9 +1455,9 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1387,9 +1472,9 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1406,9 +1491,11 @@ bb0: // invoked by strong_release it would also determine that these objects // do not escape from the function. // CHECK-LABEL: CG of check_is_local_object_through_bb_args -// CHECK-LABEL: Val %2 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %4 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4 +// CHECK-LABEL: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-LABEL: Con [int] %2.1 Esc: %7, Succ: (%2.2) +// CHECK-LABEL: Con [ref] %2.2 Esc: %7, Succ: +// CHECK-LABEL: Val [ref] %4 Esc: , Succ: (%2.1) +// CHECK-LABEL: Val [ref] %6 Esc: , Succ: %2, %4 // CHECK-LABEL: End sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): @@ -1429,7 +1516,10 @@ bb3(%6: $X): } // CHECK-LABEL: CG of check_look_through_thin_to_thick -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: %2, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: %2, Succ: // CHECK-NEXT: End sil @check_look_through_thin_to_thick: $(@convention(thin) () -> ()) -> () { bb0(%0 : $@convention(thin) () -> ()): @@ -1441,7 +1531,7 @@ bb0(%0 : $@convention(thin) () -> ()): // X.deinit // CHECK-LABEL: CG of $s4main1XCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @$s4main1XCfD: $@convention(method) (@owned X) -> () { bb0(%0 : $X): @@ -1452,11 +1542,11 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %1) -// CHECK-NEXT: Con [rc] %1 Esc: A, Succ: (%2) -// CHECK-NEXT: Con %2 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: %2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 // CHECK-NEXT: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): @@ -1485,8 +1575,9 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of call_public_external_func -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @call_public_external_func : $@convention(thin) () -> () { bb0: @@ -1515,12 +1606,13 @@ bb2: // begin_apply result) is just marked as escaping. // CHECK-LABEL: CG of call_coroutine -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %4 -// CHECK-NEXT: Val %4 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: // CHECK-NEXT: End sil @call_coroutine : $@convention(thin) () -> () { bb0: @@ -1537,15 +1629,15 @@ bb0: // Test the absence of redundant pointsTo edges // CHECK-LABEL: CG of testInitializePointsToLeaf -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %0.1) -// CHECK-NEXT: Con [rc] %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: ([rc] %13) -// CHECK-NEXT: Val %2 Esc: %4, Succ: %0.2 -// CHECK-NEXT: Val %4 Esc: %4, Succ: %2 -// CHECK-NEXT: Val %7 Esc: %12, Succ: ([rc] %13), %0.2 -// CHECK-NEXT: Val %12 Esc: %12, Succ: ([rc] %13), %7 -// CHECK-NEXT: Con [rc] %13 Esc: A, Succ: (%14) -// CHECK-NEXT: Con %14 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 +// CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) +// CHECK-NEXT: Con [ref] %14 Esc: A, Succ: // CHECK-LABEL: End class C { var c: C @@ -1593,14 +1685,14 @@ bb10(%arg2 : $LinkedNode): // a defer edge from a node with uninitialized pointsTo to a node with // already-initialized pointsTo. // CHECK-LABEL: CG of testInitializePointsToRedundant -// CHECK-NEXT: Arg %0 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Arg %1 Esc: A, Succ: ([rc] %2) -// CHECK-NEXT: Con [rc] %2 Esc: A, Succ: (%3) -// CHECK-NEXT: Con %3 Esc: A, Succ: -// CHECK-NEXT: Val %7 Esc: %7,%18, Succ: %0 -// CHECK-NEXT: Val %12 Esc: %12,%14,%18, Succ: %1 -// CHECK-NEXT: Val %14 Esc: %18, Succ: ([rc] %2), %1, %12 -// CHECK-NEXT: Val %18 Esc: %18, Succ: %7, %14 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 +// CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 +// CHECK-NEXT: Val [ref] %18 Esc: , Succ: %7, %14 // CHECK-LABEL: End sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { bb0(%0: $C, %1 : $C): @@ -1655,3 +1747,117 @@ bb10(%arg3 : $C): %66 = tuple () return %66 : $() } + +// Test canEscapeToUsePoint with defer edges between non-reference-type nodes. +// +// Unfortunately, the only way I can think of to create defer edges +// between non-reference nodes is with address-type block arguments. +// This will be banned soon, so the test will need to be disabled, but +// this is still an important corner case in the EscapeAnalysis +// logic. To keep the test working, and be able to test many more +// important corner cases, we should introduce testing modes that +// introduce spurious but predictable defer edges and node merges. +// +// Here, canEscapeTo is called on %bbarg (%5) whose node is not +// marked escaping. But it does have a defer edge to the local address +// (%0) which does cause the address to escape via the call site. +// +// CHECK-LABEL: CG of testDeferPointer +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%0.1) +// CHECK-NEXT: Val %5 Esc: , Succ: %0, %1 +// CHECK-LABEL: End +// CHECK: MayEscape: %5 = argument of bb3 : $*Builtin.Int32 +// CHECK-NEXT: to %{{.*}} = apply %{{.*}}(%0) : $@convention(thin) (@inout Builtin.Int32) -> () +sil @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + +sil hidden @testDeferPointer : $@convention(thin) () -> () { +bb0: + %iadr1 = alloc_stack $Builtin.Int32 + %iadr2 = alloc_stack $Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + br bb3(%iadr1: $*Builtin.Int32) + +bb2: + br bb3(%iadr2: $*Builtin.Int32) + +bb3(%bbarg: $*Builtin.Int32): + %val = integer_literal $Builtin.Int32, 1 + store %val to %bbarg : $*Builtin.Int32 + %ftake = function_ref @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + %call = apply %ftake(%iadr1) : $@convention(thin) (@inout Builtin.Int32) -> () + dealloc_stack %iadr2 : $*Builtin.Int32 + dealloc_stack %iadr1 : $*Builtin.Int32 + %z = tuple () + return %z : $() +} + +// Test interior node (self) cycles. + +class IntWrapper { + var property: Int64 +} + +// CHECK-LABEL: CG of testInteriorCycle +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2) +// CHECK-NEXT: Val %6 Esc: , Succ: %0, %2 +// CHECK-NEXT: Ret return Esc: , Succ: %6 +// CHECK-LABEL: End +sil @testInteriorCycle : $@convention(thin) (@owned IntWrapper) -> (Builtin.BridgeObject, UnsafeMutablePointer) { +bb0(%0 : $IntWrapper): + %bridge = unchecked_ref_cast %0 : $IntWrapper to $Builtin.BridgeObject + %adr = ref_tail_addr %0 : $IntWrapper, $Int64 + %ptr = address_to_pointer %adr : $*Int64 to $Builtin.RawPointer + %ump = struct $UnsafeMutablePointer (%ptr : $Builtin.RawPointer) + strong_release %0 : $IntWrapper + %tuple = tuple (%bridge : $Builtin.BridgeObject, %ump : $UnsafeMutablePointer) + return %tuple : $(Builtin.BridgeObject, UnsafeMutablePointer) +} + +// ============================================================================= +// Test call to array.uninitialized that has extra release_value uses + +class DummyArrayStorage { + @_hasStorage var count: Int { get } + @_hasStorage var capacity: Int { get } + init() +} + +// init_any_array_with_buffer +sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + +// CHECK-LABEL: CG of testBadArrayUninit +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1) +// CHECK-NEXT: Con %5.1 Esc: G, Succ: %10 +// CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1) +// CHECK-NEXT: Con %10.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testBadArrayUninit : $@convention(thin) (Builtin.Word, Int32) -> () { +bb0(%0 : $Builtin.Word, %1 : $Int32): + // create an array + %2 = alloc_ref [tail_elems $AnyObject * %0 : $Builtin.Word] $DummyArrayStorage + %3 = metatype $@thin Array.Type + %4 = function_ref @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %5 = apply %4(%2, %1, %3) : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %6 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 0 + %7 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 1 + %8 = struct_extract %7 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*AnyObject + + // store an elt + %10 = alloc_ref $C + %11 = init_existential_ref %10 : $C : $C, $AnyObject + store %11 to %9 : $*AnyObject + + // extra use of the call + release_value %5 : $(Array, UnsafeMutablePointer) // id: %228 + %13 = tuple () + return %13 : $() +} diff --git a/test/SILOptimizer/escape_analysis_dead_store.sil b/test/SILOptimizer/escape_analysis_dead_store.sil new file mode 100644 index 0000000000000..9a649975407cb --- /dev/null +++ b/test/SILOptimizer/escape_analysis_dead_store.sil @@ -0,0 +1,138 @@ +// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +final class X { + init() +} + +public struct S { + @_hasStorage var x: X { get set } + @_hasStorage var i: Int { get set } + init(x: X, i: Int) +} + +@_hasStorage @_hasInitialValue var gg: X { get set } + +@inline(never) func takex(_ x: X) + +sil [noinline] @takeX : $@convention(thin) (@guaranteed X) -> () + +// Test that escape analysis does not consider an inout argument to +// escape at a call site even though it's reference-type field does +// escape. Dead store elimination asks MemoryBehaviorVisitor whether +// the apply may read from the inout argument. This call into +// canEscapeToUsePoint, which should return false because the inout +// structure itself is not exposed to the call, only it's +// reference-type field is. +// +// CHECK-LABEL: sil @testInoutNoEscape +// CHECK-NOT: store +// CHECK: apply +// CHECK: store +// CHECK-NOT: store +// CHECK: } // end sil function 'testInoutNoEscape' +sil @testInoutNoEscape : $@convention(thin) (@inout S, @guaranteed S) -> () { +bb0(%0 : $*S, %1 : $S): + %4 = struct_extract %1 : $S, #S.x + %5 = struct_element_addr %0 : $*S, #S.x + %6 = load %5 : $*X + strong_retain %4 : $X + strong_release %6 : $X + store %1 to %0 : $*S + %10 = function_ref @takeX : $@convention(thin) (@guaranteed X) -> () + strong_retain %4 : $X + %12 = apply %10(%4) : $@convention(thin) (@guaranteed X) -> () + release_value %1 : $S + store %1 to %0 : $*S + %15 = tuple () + return %15 : $() +} + +// ============================================================================= +// Test that a store writing back into a container is not eliminated +// when the container's interior pointer later escapes into a function +// that reads from the pointer. + +final internal class TestArrayContainer { + @_hasStorage @_hasInitialValue internal final var pointer: UnsafeMutablePointer { get set } + @_hasStorage @_hasInitialValue internal final var storage: ContiguousArray { get set } + @_optimize(none) @inline(never) internal final func append(_ arg: Int32) + internal final func va_list() -> UnsafeMutableRawPointer + init() +} + +sil @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // user: %5 +// ContiguousArray.append(_:) +sil @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + + +// Helper that reads from a raw pointer. +sil hidden [noinline] @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool { +bb0(%0 : $UnsafeMutableRawPointer): + %1 = integer_literal $Builtin.Int64, 0 + %2 = struct $Int64 (%1 : $Builtin.Int64) + %3 = function_ref @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> + %4 = apply %3(%2, %0) : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // users: %18, %6 + %5 = integer_literal $Builtin.Int1, -1 + %6 = struct $Bool (%5 : $Builtin.Int1) + return %6 : $Bool +} + +// TestArrayContainer.append(_:) +// Helper that produces a nonempty array. +sil hidden [noinline] [Onone] @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () { +bb0(%0 : $Int32, %1 : $TestArrayContainer): + %2 = alloc_stack $Int32 + store %0 to %2 : $*Int32 + %4 = ref_element_addr %1 : $TestArrayContainer, #TestArrayContainer.storage + %5 = function_ref @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + %6 = apply %5(%2, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + dealloc_stack %2 : $*Int32 + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +// CHECK: [[ALLOC:%.*]] = alloc_ref [stack] $TestArrayContainer +// CHECK: [[PTR:%.*]] = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer +// CHECK: [[LOAD:%.*]] = load %{{.*}} : $*__ContiguousArrayStorageBase +// CHECK: [[ELTS:%.*]] = ref_tail_addr [[LOAD]] : $__ContiguousArrayStorageBase, $Int32 +// CHECK: [[ELTPTR:%.*]] = address_to_pointer [[ELTS]] : $*Int32 to $Builtin.RawPointer +// CHECK: [[UMP:%.*]] = struct $UnsafeMutablePointer ([[ELTPTR]] : $Builtin.RawPointer) +// CHECK: store [[UMP]] to [[PTR]] : $*UnsafeMutablePointer +// CHECK: [[F:%.*]] = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: apply [[F]](%{{.*}}) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: fix_lifetime %0 : $TestArrayContainer +// CHECK-LABEL: } // end sil function 'testContainerPointer' +sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +bb0: + %0 = alloc_ref [stack] $TestArrayContainer + %1 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer + %2 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.storage + %3 = integer_literal $Builtin.Int32, 42 + %4 = struct $Int32 (%3 : $Builtin.Int32) + %5 = function_ref @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %6 = apply %5(%4, %0) : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %7 = struct_element_addr %2 : $*ContiguousArray, #ContiguousArray._buffer + %8 = struct_element_addr %7 : $*_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage + %9 = load %8 : $*__ContiguousArrayStorageBase + %10 = ref_tail_addr %9 : $__ContiguousArrayStorageBase, $Int32 + %11 = address_to_pointer %10 : $*Int32 to $Builtin.RawPointer + %12 = struct $UnsafeMutablePointer (%11 : $Builtin.RawPointer) + store %12 to %1 : $*UnsafeMutablePointer + %14 = address_to_pointer %1 : $*UnsafeMutablePointer to $Builtin.RawPointer + %15 = struct $UnsafeMutableRawPointer (%14 : $Builtin.RawPointer) + %16 = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + %17 = apply %16(%15) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + fix_lifetime %0 : $TestArrayContainer + set_deallocating %0 : $TestArrayContainer + strong_release %9 : $__ContiguousArrayStorageBase + dealloc_ref %0 : $TestArrayContainer + dealloc_ref [stack] %0 : $TestArrayContainer + return %17 : $Bool +} diff --git a/test/SILOptimizer/late_release_hoisting.sil b/test/SILOptimizer/late_release_hoisting.sil new file mode 100644 index 0000000000000..9fd9653b12c26 --- /dev/null +++ b/test/SILOptimizer/late_release_hoisting.sil @@ -0,0 +1,234 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -late-release-hoisting %s | %FileCheck %s + +import Builtin +import Swift + +sil_stage canonical + +//===----------------------------------------------------------------------===// +// Unit tests for reachability to and from local objects. +//===----------------------------------------------------------------------===// + +class HasObj { + var o : AnyObject +} + +class HasInt64 { + var i : Int64 +} + +class HasHasObj { + var ho : HasObj +} + +// The release of %lo can be hoisted over the load because it does not +// point to any escaping references. +// +// CHECK-LABEL: sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasInt +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: strong_release [[LO]] : $HasInt +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalNotReachesEscaped' +sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + + %oadr = ref_element_addr %1 : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasInt64 + return %o : $AnyObject +} + +// The release of %lo cannot be hoisted over the load. +// +// CHECK-LABEL: sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[IADR]] : $*AnyObject +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release [[LO]] : $HasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscaped' +sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %lo = alloc_ref $HasObj + %iadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %iadr : $*AnyObject + + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasObj + return %o : $AnyObject +} + +// The release of %lo can be hoisted above the load from %oadr. There +// is a points-to relation between %lo and %oadr. However, the +// points-to path from %lo to %oadr reaches another reference counted +// object before reaching $oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[LO]] : $HasHasObj, #HasHasObj.ho +// CHECK: strong_retain %0 : $HasObj +// CHECK: store %0 to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[LO]] : $HasHasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCEscaped' +sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +bb0(%0 : $HasObj): + %lo = alloc_ref $HasHasObj + %hoadr = ref_element_addr %lo : $HasHasObj, #HasHasObj.ho + strong_retain %0 : $HasObj + store %0 to %hoadr : $*HasObj + %ho = load %hoadr : $*HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + // Normally a retain of %o would precede this release of %c. But + // let's assume some aggressive optimization happened. + strong_release %lo : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. +// +// TODO: We do not currently hoist strong_release %hho above +// strong_retain %o because %hho is marked as escaping. However, it is +// only stored to another local object, so in theory it does not need +// to be marked escaping. +// +// CHECK-LABEL: sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscapingLocal' +sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_retain %o : $AnyObject + strong_release %hho : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. We assume that +// the reachable one has its own reference count and hoist +// strong_release %hho above load %oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCLocal' +sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_release %hho : $HasHasObj + strong_retain %o : $AnyObject + return %o : $AnyObject +} + +// Hoist the release of an escaping object above memory operations on +// a local object that never escapes. +// +// CHECK-LABEL: sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: strong_release %1 : $HasObj +// CHECK: [[LO:%.*]] = alloc_ref $HasInt64 +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: [[I:%.*]] = load [[IADR]] : $*Int64 +// CHECK: return [[I]] : $Int64 +// CHECK-LABEL: } // end sil function 'testEscapeNotReachesLocal' +sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + %i = load %iadr : $*Int64 + strong_release %1 : $HasObj + return %i : $Int64 +} + +// EscapeAnalysis must consider the local object %lo as globally +// escaping because it is stored into an object that escapes via an +// incoming argument. This creates an aliasing relationship preventing +// the strong_release from being hoisted. +// +// CHECK-LABEL: sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject, %1 : $HasHasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HOADR:%.*]] = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release %1 : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testEscapeReachesLocal' +sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +bb0(%0 : $AnyObject, %1 : $HasHasObj): + %lo = alloc_ref $HasObj + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %oadr : $*AnyObject + %hoadr = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho + store %lo to %hoadr : $*HasObj + %o = load %oadr : $*AnyObject + strong_release %1 : $HasHasObj + return %o : $AnyObject +} diff --git a/test/SILOptimizer/mark_uninitialized_fixup.sil b/test/SILOptimizer/mark_uninitialized_fixup.sil deleted file mode 100644 index ae09df4f46d6b..0000000000000 --- a/test/SILOptimizer/mark_uninitialized_fixup.sil +++ /dev/null @@ -1,117 +0,0 @@ -// RUN: %target-sil-opt -mark-uninitialized-fixup %s | %FileCheck %s - -sil_stage raw - -import Builtin - -// CHECK-LABEL: sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: project_box [[BOX]] -sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - - -// CHECK-LABEL: sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: br [[NEXT_BB:bb[0-9]+]] -// -// CHECK: [[NEXT_BB]]: -// CHECK: project_box [[BOX]] -sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - br bb1 - -bb1: - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -struct Bool { - var _value: Builtin.Int1 -} - -// CHECK-LABEL: sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: struct_element_addr [[MUI]] -// CHECK: load [trivial] [[MUI]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -bb0(%0 : $Builtin.Int1, %1 : $@thin Bool.Type): - %2 = alloc_box ${ var Bool }, var, name "self" - %3 = mark_uninitialized [rootself] %2 : ${ var Bool } - %4 = project_box %3 : ${ var Bool }, 0 - debug_value %0 : $Builtin.Int1, let, name "v", argno 1 - %6 = struct_element_addr %4 : $*Bool, #Bool._value - assign %0 to %6 : $*Builtin.Int1 - %8 = load [trivial] %4 : $*Bool - destroy_value %3 : ${ var Bool } - return %8 : $Bool -} - -// This is a simulation of the type of callee generated by capture promotion. -sil @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - -// CHECK-LABEL: sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -// CHECK: bb0([[ARG:%.*]] : $Builtin.Int1): -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool }, var, name "self" -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: [[BOX_COPY:%.*]] = copy_value [[BOX]] -// CHECK: [[PB_BOX_COPY:%.*]] = project_box [[BOX_COPY]] -// CHECK: destroy_value [[BOX_COPY]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -bb0(%0 : $Builtin.Int1): - // Initial initialization. - %1 = alloc_box ${ var Bool }, var, name "self" - %2 = mark_uninitialized [rootself] %1 : ${ var Bool } - %3 = project_box %2 : ${ var Bool }, 0 - %4 = struct_element_addr %3 : $*Bool, #Bool._value - store %0 to [trivial] %4 : $*Builtin.Int1 - - // copy + project_box for function_call. This can happen via - // capture_promotion. - %5 = function_ref @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - %6 = copy_value %2 : ${ var Bool } - %7 = project_box %6 : ${ var Bool }, 0 - %8 = apply %5(%7) : $@convention(thin) (@in Bool) -> () - destroy_value %6 : ${ var Bool } - - // Epilog - destroy_value %2 : ${ var Bool } - %9999 = tuple() - return %9999 : $() -} diff --git a/test/SILOptimizer/merge_exclusivity.swift b/test/SILOptimizer/merge_exclusivity.swift index 9e995df212d7d..986c69d1d7525 100644 --- a/test/SILOptimizer/merge_exclusivity.swift +++ b/test/SILOptimizer/merge_exclusivity.swift @@ -372,13 +372,10 @@ private struct EscapedTransforme: WriteProt { // TESTSIL-NEXT: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFADDR]] // TESTSIL: end_access [[B1]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity14run_MergeTest9yySiF' @inline(never) diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index e18ace13123ac..d88adf2eb0558 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -428,7 +428,7 @@ bb0: // CHECK-LABEL: sil [ossa] @dead_allocation_1 sil [ossa] @dead_allocation_1 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 +// CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional store %0 to [init] %2 : $*Optional @@ -438,6 +438,7 @@ bb0(%0 : @owned $Optional): dealloc_stack %2 : $*Optional destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional +// CHECK: destroy_value %0 %3 = tuple () return %3 : $() } @@ -445,7 +446,6 @@ bb0(%0 : @owned $Optional): // CHECK-LABEL: sil [ossa] @dead_allocation_2 sil [ossa] @dead_allocation_2 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 // CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional @@ -457,6 +457,7 @@ bb0(%0 : @owned $Optional): destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional %3 = tuple () +// CHECK: destroy_value %0 return %3 : $() } diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 854c5f85f02d6..2c4e0e9bafeed 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -9,6 +9,10 @@ import Builtin ////////////////// enum MyNever {} +enum FakeOptional { +case none +case some(T) +} sil @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () sil @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> () @@ -24,6 +28,7 @@ sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair class Klass {} sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () +sil @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () struct MyInt { var value: Builtin.Int32 @@ -40,11 +45,6 @@ struct StructMemberTest { var t : (Builtin.Int32, AnotherStruct) } -enum FakeOptional { -case none -case some(T) -} - class ClassLet { @_hasStorage let aLet: Klass @_hasStorage var aVar: Klass @@ -589,6 +589,46 @@ bb0(%x : @owned $ClassLet): return undef : $() } +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : +// CHECK: load_borrow +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLetTuple + %v = load [copy] %p : $*(Klass, Klass) + (%v1, %v2) = destructure_tuple %v : $(Klass, Klass) + apply %f(%v1) : $@convention(thin) (@guaranteed Klass) -> () + apply %f(%v2) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %v1 : $Klass + destroy_value %v2 : $Klass + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : +// CHECK: load_borrow +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLet + %v = load [copy] %p : $*Klass + %v_cast = unchecked_ref_cast %v : $Klass to $Builtin.NativeObject + apply %f(%v_cast) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %v_cast : $Builtin.NativeObject + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr @@ -662,7 +702,7 @@ bb0(%x : @owned $ClassLet): return undef : $() } -// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates +// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: [[INNER:%.*]] = load_borrow @@ -672,12 +712,15 @@ bb0(%x : @owned $ClassLet): // CHECK-NEXT: begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: load [copy] -// CHECK-NEXT: apply // CHECK-NEXT: end_borrow // CHECK-NEXT: destroy_value +// CHECK-NEXT: // function_ref +// CHECK-NEXT: function_ref +// CHECK-NEXT: enum // CHECK-NEXT: apply // CHECK-NEXT: destroy_value -sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates : $@convention(thin) (@owned ClassLet) -> () { +// CHECK: } // end sil function 'do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2' +sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : $@convention(thin) (@owned ClassLet) -> () { bb0(%x : @owned $ClassLet): %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () @@ -693,18 +736,17 @@ bb0(%x : @owned $ClassLet): %b = begin_borrow %x : $ClassLet %q = ref_element_addr %b : $ClassLet, #ClassLet.aLet %w = load [copy] %q : $*Klass - %d = begin_borrow %w : $Klass - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () // End the lifetime of the base object first... end_borrow %b : $ClassLet destroy_value %x : $ClassLet // ...then end the lifetime of the copy. - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () + %f2 = function_ref @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () + %w2 = enum $FakeOptional, #FakeOptional.some!enumelt.1, %w : $Klass + apply %f2(%w2) : $@convention(thin) (@guaranteed FakeOptional) -> () - end_borrow %d : $Klass - destroy_value %w : $Klass + destroy_value %w2 : $FakeOptional return undef : $() } diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index f7e49003c9333..b202d5fbd0f63 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -101,7 +101,7 @@ struct SS: PPP { // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() - p.returnsOptionalIndirect()?.returnsOptionalIndirect() + _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() } //===----------------------------------------------------------------------===// diff --git a/test/Sema/diag_originally_definedin.swift b/test/Sema/diag_originally_definedin.swift new file mode 100644 index 0000000000000..90fd72161bb31 --- /dev/null +++ b/test/Sema/diag_originally_definedin.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx + +@_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} +public func foo() {} + +@available(macOS 10.13, *) +@_originallyDefinedIn(module: "original", OSX 10.12) // expected-error {{moved version from @_originallyDefinedIn must after introduced OS version}} +public class C { + @_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{@_originallyDefinedIn is only applicable to top-level decl}} + public func foo() {} +} diff --git a/test/Serialization/Inputs/opaque_types_inlineable_2.swift b/test/Serialization/Inputs/opaque_types_inlineable_2.swift new file mode 100644 index 0000000000000..1a20580f22f57 --- /dev/null +++ b/test/Serialization/Inputs/opaque_types_inlineable_2.swift @@ -0,0 +1,18 @@ +public protocol P {} + +public struct M : P { + public init(t: T) {} +} +extension Int : P {} + +extension P { + @inlinable + public func o(_ t: T) -> some P { + return M(t: t) + } + + @inlinable + public func p() throws -> some P { + return Int() + } +} diff --git a/test/Serialization/Recovery/missing-clang-module.swift b/test/Serialization/Recovery/missing-clang-module.swift new file mode 100644 index 0000000000000..248bb8922c471 --- /dev/null +++ b/test/Serialization/Recovery/missing-clang-module.swift @@ -0,0 +1,32 @@ +//// Report dependency cycles involving missing clang modules without crashing +//// rdar://problem/57364033 + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/custom-modules %t/ + +// RUN: %target-swift-frontend -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule +// RUN: %target-swift-frontend -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t -I %t/custom-modules +// RUN: %target-swift-frontend -emit-module -DLIB_C %s -module-name C -emit-module-path %t/C.swiftmodule -I %t + +//// Delete the clang module +// RUN: rm -r %t/custom-modules/ + +// RUN: not %target-swift-frontend -emit-module -DLIB_D %s -module-name A -emit-module-path %t/D.swiftmodule -I %t 2>&1 | %FileCheck %s + +#if LIB_A + +#elseif LIB_B + +import IndirectImport // From custom-modules +import A + +#elseif LIB_C + +import B + +#elseif LIB_D + +import C +// CHECK: :0: error: circular dependency between modules 'A' and 'B' + +#endif diff --git a/test/Serialization/opaque_types_inlineable.swift b/test/Serialization/opaque_types_inlineable.swift new file mode 100644 index 0000000000000..504d6b148273a --- /dev/null +++ b/test/Serialization/opaque_types_inlineable.swift @@ -0,0 +1,29 @@ +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -module-name A -emit-module %s %S/Inputs/opaque_types_inlineable_2.swift + +// This test case use to crash in the merge modules phase when the two partial +// modules are merged as one deserializing the module for this file now has +// access to opaque types in the other file (opaque_types_inlineable_2.swift). + +extension P { + @inlinable + public func r() -> some P { + return f { self.o(Q()) } + } + + @inlinable + public func q() throws -> some P { + return try p() + } +} + +public func f(_ fn: () -> T) -> some P { + return K() +} + +public struct K : P { + public init() {} +} + +public struct Q : P { + public init() {} +} diff --git a/test/SourceKit/CodeComplete/complete_sequence.swift b/test/SourceKit/CodeComplete/complete_sequence.swift new file mode 100644 index 0000000000000..1b1c8c8e6a1d1 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence.swift @@ -0,0 +1,65 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Tests for 'key.codecomplete.reuseastcontex' option. + +// Disabled. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -pos=12:11 %s -- %s == \ +// RUN: -req=complete -pos=15:11 %s -- %s > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response + +// Enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- %s > %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=TRACE_REUSEAST %s < %t.response.reuseastcontext + +// Enabled - compiler argument mismatch. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s -DNOTUSED == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- -DNOTUSED %s > %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response.reuseastcontext_argmismatch + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" + +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift new file mode 100644 index 0000000000000..bf7ac0fad0117 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift @@ -0,0 +1,55 @@ +// BEGIN file1.swift +class Foo { + var x: Int + var y: Int +} + +func foo(arg: Foo) { + _ = arg. +} + +class Bar { + var a: Int + var b: Int + func barMethod() {} +} + +// BEGIN file2.swift +extension Foo { + func fooMethod() {} +} + +func bar(arg: Bar) { + _ = arg. +} + +extension Bar { + func barMethod() {} +} + +// BEGIN dummy.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=7:11 %t/file1.swift -- %t/file1.swift %t/file2.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=6:11 %t/file2.swift -- %t/file1.swift %t/file2.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_edit.swift b/test/SourceKit/CodeComplete/complete_sequence_edit.swift new file mode 100644 index 0000000000000..37dae86946b5b --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_edit.swift @@ -0,0 +1,89 @@ +// BEGIN State1.swift +// Initial state. +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State2.swift +// Compatible change: implemented 'Foo.fooMethod()', indentation change, added white line. +class Foo { + var x: Int + var y: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State3.swift +// Incompatible change: added 'Foo.z' +class Foo { + var x: Int + var y: Int + var z: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN DUMMY.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=8:11 -name file.swift -text-input %t/State1.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=11:13 -name file.swift -text-input %t/State2.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:13 -name file.swift -text-input %t/State3.swift -- file.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT-DAG: key.name: "z" +// RESULT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] diff --git a/test/SourceKit/CodeComplete/complete_sequence_localvar.swift b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift new file mode 100644 index 0000000000000..49a27534782f8 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift @@ -0,0 +1,26 @@ +func test() { + var localVar = 1 + + var afterVar = 1 +} + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s | %FileCheck %s + +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" diff --git a/test/SourceKit/CodeComplete/complete_sequence_race.swift b/test/SourceKit/CodeComplete/complete_sequence_race.swift new file mode 100644 index 0000000000000..47cb1b147cc49 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_race.swift @@ -0,0 +1,48 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Test for simultaneous completion requests don't cause compiler crashes. + +// ReuseASTContext disabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s + +// ReuseASTContext enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s diff --git a/test/SymbolGraph/Module.swift b/test/SymbolGraph/Module.swift new file mode 100644 index 0000000000000..dcfba01e9a43b --- /dev/null +++ b/test/SymbolGraph/Module.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SymbolGraphModule -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SymbolGraphModule -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: module +// CHECK-NEXT: "name": "SymbolGraphModule" +// CHECK-NEXT: platform +// CHECK-NEXT: architecture +// CHECK: vendor +// CHECK-NEXT: operatingSystem +// CHECK-NEXT: name +// CHECK-NEXT: minimumVersion +// CHECK-NEXT: major +// CHECK-NEXT: minor +// CHECK-NEXT: patch diff --git a/test/SymbolGraph/Relationships/ConformsTo.swift b/test/SymbolGraph/Relationships/ConformsTo.swift new file mode 100644 index 0000000000000..c895fd5f7bfc6 --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +public struct S: P { + public var x: Int +} + +// CHECK: "kind": "conformsTo" +// CHECK-NEXT: "source": "s:10ConformsTo1SV" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift new file mode 100644 index 0000000000000..3193cfdf15200 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DefaultImplementationOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DefaultImplementationOf -I %t -pretty-print -o %t/DefaultImplementationOf.symbols.json +// RUN: %FileCheck %s --input-file %t/DefaultImplementationOf.symbols.json + +public protocol P { + var x: Int { get } +} + +extension P { + public var x: Int { + return 2 + } +} + +// CHECK: "kind": "defaultImplementationOf", +// CHECK-NEXT: "source": "s:23DefaultImplementationOf1PPAAE1xSivp", +// CHECK-NEXT: "target": "s:23DefaultImplementationOf1PP1xSivp" diff --git a/test/SymbolGraph/Relationships/InheritsFrom.swift b/test/SymbolGraph/Relationships/InheritsFrom.swift new file mode 100644 index 0000000000000..feb9c4f9ec61b --- /dev/null +++ b/test/SymbolGraph/Relationships/InheritsFrom.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InheritsFrom -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InheritsFrom -I %t -pretty-print -o %t/InheritsFrom.symbols.json +// RUN: %FileCheck %s --input-file %t/InheritsFrom.symbols.json + +public class Base {} +public class Derived: Base {} + +// CHECK: "kind": "inheritsFrom" +// CHECK-NEXT: "source": "s:12InheritsFrom7DerivedC" +// CHECK-NEXT: "target": "s:12InheritsFrom4BaseC" diff --git a/test/SymbolGraph/Relationships/MemberOf.swift b/test/SymbolGraph/Relationships/MemberOf.swift new file mode 100644 index 0000000000000..f338e74aff62f --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MemberOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MemberOf -I %t -pretty-print -o %t/MemberOf.symbols.json +// RUN: %FileCheck %s --input-file %t/MemberOf.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: "kind": "memberOf" +// CHECK-NEXT: "source": "s:8MemberOf1SV1xSivp" +// CHECK-NEXT: "target": "s:8MemberOf1SV" diff --git a/test/SymbolGraph/Relationships/Overrides.swift b/test/SymbolGraph/Relationships/Overrides.swift new file mode 100644 index 0000000000000..85d2f7afb57f2 --- /dev/null +++ b/test/SymbolGraph/Relationships/Overrides.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Overrides -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Overrides -I %t -pretty-print -o %t/Overrides.symbols.json +// RUN: %FileCheck %s --input-file %t/Overrides.symbols.json + +public class Base { + public var x: Int { + return 1 + } +} + +public class Derived: Base { + public override var x: Int { + return 2 + } +} + +// CHECK: "kind": "overrides" +// CHECK-NEXT: "source": "s:9Overrides7DerivedC1xSivp" +// CHECK-NEXT: "target": "s:9Overrides4BaseC1xSivp" diff --git a/test/SymbolGraph/Relationships/RequirementOf.swift b/test/SymbolGraph/Relationships/RequirementOf.swift new file mode 100644 index 0000000000000..a06507b523b7e --- /dev/null +++ b/test/SymbolGraph/Relationships/RequirementOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +// CHECK: "kind": "requirementOf" +// CHECK-NEXT: "source": "s:10ConformsTo1PP1xSivp" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/TargetFallback.swift b/test/SymbolGraph/Relationships/TargetFallback.swift new file mode 100644 index 0000000000000..274c423d0f748 --- /dev/null +++ b/test/SymbolGraph/Relationships/TargetFallback.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name TargetFallback -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name TargetFallback -I %t -pretty-print -o %t/TargetFallback.symbols.json +// RUN: %FileCheck %s --input-file %t/TargetFallback.symbols.json + +public struct S: CustomStringConvertible { + public var x: Int + public var description: String { + return x.description + } +} + +// CHECK: "kind": "conformsTo", +// CHECK-NEXT: "source": "s:14TargetFallback1SV", +// CHECK-NEXT: "target": "s:s23CustomStringConvertibleP", +// CHECK-NEXT: "targetFallback": "Swift.CustomStringConvertible" diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift new file mode 100644 index 0000000000000..eb6e3bb15633f --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -o %t/IncludeInternal.symbols.json -minimum-access-level internal +// RUN: %FileCheck %s --input-file %t/IncludeInternal.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldAlsoAppear { + internal var x: Int +} + +private struct ShouldntAppear { + var x: Int +} + +// CHECK: ShouldAppear +// CHECK: ShouldAlsoAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift new file mode 100644 index 0000000000000..258b29058f1c1 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name PublicDefault -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name PublicDefault -I %t -pretty-print -o %t/PublicDefault.symbols.json +// RUN: %FileCheck %s --input-file %t/PublicDefault.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldntAppear { + internal var x: Int +} + +// CHECK: ShouldAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevels.swift b/test/SymbolGraph/Symbols/AccessLevels.swift new file mode 100644 index 0000000000000..e7d3daec46132 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevels.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name AccessLevels -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name AccessLevels -I %t -pretty-print -o %t/AccessLevels.symbols.json +// RUN: %FileCheck %s --input-file %t/AccessLevels.symbols.json + +// CHECK: "accessLevel": "public" + +public struct PublicStruct { + public var x: Int +} + +// CHECK-NOT: "accessLevel": "private" diff --git a/test/SymbolGraph/Symbols/DocComment.swift b/test/SymbolGraph/Symbols/DocComment.swift new file mode 100644 index 0000000000000..b9c07d2df1548 --- /dev/null +++ b/test/SymbolGraph/Symbols/DocComment.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DocComment -emit-module-path %t/DocComment.swiftmodule +// RUN: %target-swift-symbolgraph-extract -module-name DocComment -I %t -pretty-print -o %t/DocComment.symbols.json +// RUN: %FileCheck %s --input-file %t/DocComment.symbols.json + +// CHECK: "text": "Single line." + +/// Single line. +public struct S1 {} + +// CHECK: "text": "Two" +// CHECK: "text": "lines." + +/// Two +/// lines. +public struct S2 {} + +// CHECK: "text": "There are" +// CHECK: "text": "three lines" +// CHECK: "text": "here." + +/// There are +/// three lines +/// here. +public struct S3 {} + +// CHECK: "text": "Comment" +// CHECK: "text": "block." + +/** + Comment + block. +*/ +public struct S4 {} + +// CHECK: "text": "Comment block" +// CHECK: "text": "with more indentation." + +/** + Comment block + with more indentation. + */ +public struct S5 {} + +// CHECK: "text": "With" +// CHECK: "text": "ASCII" +// CHECK: "text": "art." + +/** + * With + * ASCII + * art. + */ +public struct S6 {} diff --git a/test/SymbolGraph/Symbols/Kinds.swift b/test/SymbolGraph/Symbols/Kinds.swift new file mode 100644 index 0000000000000..ec79235016659 --- /dev/null +++ b/test/SymbolGraph/Symbols/Kinds.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Kinds -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Kinds -I %t -pretty-print -o %t/Kinds.symbols.json +// RUN: %FileCheck %s --input-file %t/Kinds.symbols.json + +// CHECK: "identifier": "swift.class" +// CHECK-NEXT: "displayName": "Class" +public class C {} + +// CHECK: "identifier": "swift.struct" +// CHECK-NEXT: "displayName": "Structure" +public struct S { + // CHECK: "identifier": "swift.variable" + // CHECK-NEXT: "displayName": "Variable" + public var x: Int + + // CHECK: "identifier": "swift.initializer" + // CHECK-NEXT: "displayName": "Initializer" + public init(x: Int) { + self.x = x + } + + // CHECK: "identifier": "swift.function" + // CHECK-NEXT: "displayName": "Function" + public func foo() {} +} + +// CHECK: "identifier": "swift.enum" +// CHECK-NEXT: "displayName": "Enumeration" +public enum E { + // CHECK: "identifier": "swift.enum.case" + // CHECK-NEXT: "displayName": "Case" + case oneCase +} + +// CHECK: "identifier": "swift.protocol" +// CHECK-NEXT: "displayName": "Protocol" +public protocol P {} + +// CHECK: "identifier": "swift.typealias" +// CHECK-NEXT: "displayName": "Type Alias" +public typealias Alias = S diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift new file mode 100644 index 0000000000000..d380b84e5d766 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Availability -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Availability -I %t -pretty-print -o %t/Availability.symbols.json +// RUN: %FileCheck %s --input-file %t/Availability.symbols.json + +@available(macOS, introduced: 10.9, deprecated: 10.10, obsoleted: 10.11, message: "Everyone makes mistakes", renamed: "S2") +public struct S {} + +// CHECK: "domain": "macOS" + +// CHECK: introduced +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 9 + +// CHECK: deprecated +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 10 + +// CHECK: obsoleted +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 11 + +// CHECK: "message": "Everyone makes mistakes" +// CHECK: "renamed": "S2" diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift new file mode 100644 index 0000000000000..96488cbc63d20 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyDeprecated -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyDeprecated -I %t -pretty-print -o %t/UnconditionallyDeprecated.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyDeprecated.symbols.json + +@available(*, deprecated) +public struct UnconditionallyDeprecated {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyDeprecated": true diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift new file mode 100644 index 0000000000000..9fdcb6fa779f9 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyUnavailable -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyUnavailable -I %t -pretty-print -o %t/UnconditionallyUnavailable.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyUnavailable.symbols.json + +@available(*, unavailable) +public struct UnconditionallyUnavailable {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyUnavailable": true diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift new file mode 100644 index 0000000000000..9f35f6fde8b5e --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeclarationFragments -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeclarationFragments -I %t -pretty-print -o %t/DeclarationFragments.symbols.json +// RUN: %FileCheck %s --input-file %t/DeclarationFragments.symbols.json + +public func foo(f: @escaping () -> (), x: Int = 2, s: S) {} + +// CHECK: declarationFragments + +// CHECK: "kind": "keyword", +// CHECK-NEXT: "spelling": "func" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "foo" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": "<" + +// CHECK: "kind": "genericParameter", +// CHECK-NEXT: "spelling": "S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ">(" + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "f" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": () -> (), " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "x" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": " + +// CHECK: "kind": "typeIdentifier", +// CHECK-NEXT: "spelling": "Int", +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " = 2, " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "s" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ")" diff --git a/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift new file mode 100644 index 0000000000000..ae2a4bd6c441a --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name FunctionSignature -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name FunctionSignature -I %t -pretty-print -o %t/FunctionSignature.symbols.json +// RUN: %FileCheck %s --input-file %t/FunctionSignature.symbols.json + +public func foo(_ noext: Int, ext int: Int) -> String { + return "OK" +} + +// CHECK: "name": "noext" +// CHECK-NOT: "internalName": "noext" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "noext" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "name": "ext" +// CHECK-NEXT: "internalName": "int" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "int" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: returns +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "String" +// CHECK-NEXT: "preciseIdentifier": "s:SS" diff --git a/test/SymbolGraph/Symbols/Names.swift b/test/SymbolGraph/Symbols/Names.swift new file mode 100644 index 0000000000000..4ea658167d919 --- /dev/null +++ b/test/SymbolGraph/Symbols/Names.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -o %t/Names.symbols.json +// RUN: %FileCheck %s --input-file %t/Names.symbols.json + +public struct MyStruct {} + +// CHECK: names +// CHECK-NEXT: "title": "MyStruct" diff --git a/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift new file mode 100644 index 0000000000000..e62567f069c4c --- /dev/null +++ b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SkipsPublicUnderscore -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SkipsPublicUnderscore -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct _ShouldntAppear { + public var shouldntAppear: Int +} + +// CHECK-NOT: _ShouldntAppear +// CHECK-NOT: shouldntAppear diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift new file mode 100644 index 0000000000000..3e5b9e11c88e0 --- /dev/null +++ b/test/TBD/linker-directives.swift @@ -0,0 +1,25 @@ +// REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "ToasterKit", macOS 10.15) +public func toast() {} + +// CHECK-HAS: $ld$hide$os10.14$_$s10ToasterKit5toastyyF +// CHECK-HAS: $ld$hide$os10.8$_$s10ToasterKit5toastyyF + +// CHECK-HAS-NOT-NOT: $ld$hide$os10.15$_$s10ToasterKit5toastyyF +// CHECK-HAS-NOT-NOT: $ld$hide$os10.7$_$s10ToasterKit5toastyyF + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -emit-ldadd-cfile-path %t/ldAdd.c -module-name AppKit +// RUN: %FileCheck %s --check-prefix CHECK-C-SYMBOL < %t/ldAdd.c + +// CHECK-C-SYMBOL: $ld$add$os10.14$_$s10ToasterKit5toastyyF +// CHECK-C-SYMBOL: $ld$add$os10.8$_$s10ToasterKit5toastyyF \ No newline at end of file diff --git a/test/api-digester/Inputs/cake_baseline/cake.swift b/test/api-digester/Inputs/cake_baseline/cake.swift index fd57d1823f41e..8ac2884bca90a 100644 --- a/test/api-digester/Inputs/cake_baseline/cake.swift +++ b/test/api-digester/Inputs/cake_baseline/cake.swift @@ -105,7 +105,26 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval: C3 {} -public class ClassToStruct {} +public class ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + internal init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + public init() {} + public convenience init(x: Int) { self.init() } +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public protocol ProtocolToEnum {} public class SuperClassChange: C7 {} diff --git a/test/api-digester/Inputs/cake_current/cake.swift b/test/api-digester/Inputs/cake_current/cake.swift index d9dee4768a10f..1f9a0a772298e 100644 --- a/test/api-digester/Inputs/cake_current/cake.swift +++ b/test/api-digester/Inputs/cake_current/cake.swift @@ -114,7 +114,30 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval {} -public struct ClassToStruct {} +public struct ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + // Remove the @_hasMissingDesignatedInitializers attribute + public init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + // Add the @_hasMissingDesignatedInitializers attribute by adding an inaccessible + // init + public init() {} + public convenience init(x: Int) { self.init() } + internal init(y: Int) {} +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public enum ProtocolToEnum {} public class SuperClassChange: C8 {} diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index f19c8589fdd62..fb4d8c7a8926f 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -60,6 +60,8 @@ cake: Class C0 is a new API without @available attribute cake: Class C5 is now without @objc cake: Class C8 is a new API without @available attribute cake: Constructor C1.init(_:) is a new API without @available attribute +cake: Constructor ClassWithMissingDesignatedInits.init() is a new API without @available attribute +cake: Constructor SubclassWithMissingDesignatedInits.init() is a new API without @available attribute cake: Enum IceKind is now without @frozen cake: EnumElement FrozenKind.AddedCase is a new API without @available attribute cake: Func C1.foo1() is now not static @@ -102,10 +104,10 @@ cake: Struct fixedLayoutStruct has added a conformance to an existing protocol P cake: Struct fixedLayoutStruct has removed conformance to P1 /* Protocol Requirement Change */ -cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry +cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry cake: AssociatedType AssociatedTypePro.T1 has removed default type Swift.Int cake: AssociatedType RequiementChanges.addedTypeWithoutDefault has been added as a protocol requirement -cake: Func HasMutatingMethodClone.foo() now requires new witness table entry +cake: Func HasMutatingMethodClone.foo() now requires new witness table entry cake: Func RequiementChanges.addedFunc() has been added as a protocol requirement cake: Var RequiementChanges.addedVar has been added as a protocol requirement @@ -113,4 +115,6 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/Outputs/Cake.txt b/test/api-digester/Outputs/Cake.txt index 97f48480f01cf..0dab4e4589584 100644 --- a/test/api-digester/Outputs/Cake.txt +++ b/test/api-digester/Outputs/Cake.txt @@ -64,7 +64,9 @@ cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclass cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class cake: Func ClassWithOpenMember.bar() is no longer open for subclassing cake: Func ClassWithOpenMember.foo() is no longer open for subclassing cake: Var ClassWithOpenMember.property is no longer open for subclassing diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index ac91076d139a7..a6f9baf090553 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -199,7 +199,8 @@ "usr": "s:4cake2C0C", "moduleName": "cake", "genericSig": "<τ_0_0, τ_0_1, τ_0_2>", - "sugared_genericSig": "" + "sugared_genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -428,6 +429,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1330,7 +1333,8 @@ "declAttributes": [ "FixedLayout", "UsableFromInline" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1387,6 +1391,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1418,7 +1423,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1430,7 +1436,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1482,7 +1489,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index a2e3fa2407b12..13fd939c1e866 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -201,7 +201,8 @@ "declKind": "Class", "usr": "s:4cake2C0C", "moduleName": "cake", - "genericSig": "" + "genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeAlias", @@ -425,6 +426,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1235,6 +1238,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1266,7 +1270,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1278,7 +1283,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1330,7 +1336,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 80da5e122c3ba..d6d9a22418752 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -119,6 +119,7 @@ "Dynamic" ], "superclassUsr": "c:objc(cs)NSObject", + "inheritsConvenienceInitializers": true, "superclassNames": [ "ObjectiveC.NSObject" ], @@ -134,6 +135,24 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" + }, + { + "kind": "Conformance", + "name": "Equatable", + "printedName": "Equatable", + "usr": "s:SQ" + }, + { + "kind": "Conformance", + "name": "Hashable", + "printedName": "Hashable", + "usr": "s:SH" + }, + { + "kind": "Conformance", + "name": "CVarArg", + "printedName": "CVarArg", + "usr": "s:s7CVarArgP" } ] }, diff --git a/test/api-digester/compare-dump.swift b/test/api-digester/compare-dump.swift index 54574cbb1a7e4..c37cecb5d424f 100644 --- a/test/api-digester/compare-dump.swift +++ b/test/api-digester/compare-dump.swift @@ -4,8 +4,8 @@ // RUN: %empty-directory(%t.module-cache) // RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -module-name cake // RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -module-name cake -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft > %t.dump1.json -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesLeft > %t.dump2.json +// RUN: %api-digester -dump-sdk -module cake -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft +// RUN: %api-digester -dump-sdk -module cake -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesRight // RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result // RUN: %clang -E -P -x c %S/Outputs/Cake.txt -o - | sed '/^\s*$/d' > %t.expected diff --git a/test/attr/Inputs/SymbolMove/LowLevel.swift b/test/attr/Inputs/SymbolMove/LowLevel.swift index 5a42275802f46..fcc17c74e7f5f 100644 --- a/test/attr/Inputs/SymbolMove/LowLevel.swift +++ b/test/attr/Inputs/SymbolMove/LowLevel.swift @@ -1,9 +1,11 @@ -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public func printMessageMoved() { print("Hello from LowLevel") } -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public struct Entity { public let value = "LowLevel" public init() {} @@ -11,7 +13,8 @@ public struct Entity { } // =================== Move protocol =================================// -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public protocol Box { associatedtype Item var ItemKind: String { get } @@ -19,19 +22,22 @@ public protocol Box { func shape() -> String } -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) extension Box { public func shape() -> String { return "round"} } -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public struct Candy { public var kind = "candy" public init() {} } // =================== Move enum ============================ // -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) public enum LanguageKind: Int { case Cpp = 1 case Swift = 2 @@ -39,7 +45,8 @@ public enum LanguageKind: Int { } // =================== Move class ============================ // -@_originallyDefinedIn(module: "HighLevel", OSX 10.10) +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) open class Vehicle { public init() {} public var currentSpeed = 40.0 diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 4c136c330414e..a180473605531 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -swift-version 5 // Simple case. -var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} expected-error {{cannot convert value of type 'Int' to specified type '() -> Int'}} +var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} @autoclosure func func1() {} // expected-error {{attribute can only be applied to types, not declarations}} @@ -98,9 +98,9 @@ class TestFunc12 { func test() { func12a(x + foo()) // okay - func12c(x + foo()) - // expected-error@-1{{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{13-13=self.}} - // expected-error@-2{{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{17-17=self.}} + func12c(x + foo()) + // expected-error@-1{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-1{{reference 'self.' explicitly}} {{13-13=self.}} + // expected-error@-2{{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-2{{reference 'self.' explicitly}} {{17-17=self.}} } } @@ -281,3 +281,17 @@ func test_autoclosure_with_generic_argument_mismatch() { foo(S()) // expected-error {{cannot convert value of type 'S' to expected argument type 'S'}} } + +// SR-11934 +func sr_11934(_ x: @autoclosure String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} + +// SR-11938 +let sr_11938_1: Array<@autoclosure String> = [] // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_2() -> @autoclosure String { "" } // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_3(_ x: [@autoclosure String]) {} // expected-error {{'@autoclosure' may only be used on parameters}} + +protocol SR_11938_P {} +struct SR_11938_S : @autoclosure SR_11938_P {} // expected-error {{'@autoclosure' may only be used on parameters}} + +// SR-9178 +func bar(_ x: @autoclosure T) {} // expected-error 1{{@autoclosure attribute only applies to function types}} diff --git a/test/attr/attr_convention.swift b/test/attr/attr_convention.swift index 7ee849bdb3079..83048435363fc 100644 --- a/test/attr/attr_convention.swift +++ b/test/attr/attr_convention.swift @@ -2,8 +2,12 @@ let f1: (Int) -> Int = { $0 } let f2: @convention(swift) (Int) -> Int = { $0 } +let f2a: @convention(swift, cType: "int *(int)") (Int32) -> Int32 = { $0 } // expected-error{{convention 'swift' does not support the 'cType' argument label, did you mean @convention(c, cType: "int *(int)") or @convention(block, cType: "int *(int)") instead?}} let f3: @convention(block) (Int) -> Int = { $0 } let f4: @convention(c) (Int) -> Int = { $0 } +let f4a: @convention(c, cType: "int (int)") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'int (int)'; it should be a C function pointer type or a block pointer type}} +let f4b: @convention(c, cType: "void *") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'void *'; it should be a C function pointer type or a block pointer type}} +let f4c: @convention(c, cType: "int (*)(int)") (Int32) -> Int32 = { $0 } let f5: @convention(INTERCAL) (Int) -> Int = { $0 } // expected-error{{convention 'INTERCAL' not supported}} diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index d4caa72601b2a..16ce674ec24ed 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -226,3 +226,6 @@ extension SR_9760 { func fiz(_: T, _: @escaping F) {} // Ok func baz(_: @escaping G) {} // Ok } + +// SR-9178 +func foo(_ x: @escaping T) {} // expected-error 1{{@escaping attribute only applies to function types}} diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index d22bccde0f388..1414a1463bb08 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -6,7 +6,7 @@ func conflictingAttrs(_ fn: @noescape @escaping () -> Int) {} // expected-error func doesEscape(_ fn : @escaping () -> Int) {} -func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error 2{{unknown attribute 'noescape'}} +func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error {{unknown attribute 'noescape'}} var globalAny: Any = 0 @@ -23,8 +23,8 @@ func takesVariadic(_ fns: () -> Int...) { doesEscape(fns[0]) // Okay - variadic-of-function parameters are escaping } -func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note 2 {{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} - // expected-note@-1 5{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} +func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note 1 {{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} + // expected-note@-1 6{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} takesNoEscapeClosure { 4 } // ok _ = fn() // ok @@ -52,7 +52,7 @@ class SomeClass { func test() { // This should require "self." - doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} // Since 'takesNoEscapeClosure' doesn't escape its closure, it doesn't // require "self." qualification of member references. @@ -64,88 +64,89 @@ class SomeClass { foo() func plain() { foo() } - let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} _ = plain2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{31-31=self.}} - _ = mulit2 + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{30-30= [self] in}} expected-note{{reference 'self.' explicitly}} {{31-31=self.}} + _ = multi2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo(); return 0 } // okay func outer() { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{28-28=self.}} - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{27-27= [self] in}} expected-note{{reference 'self.' explicitly}} {{28-28=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } } - let _: () -> Void = { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Void = { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let _ = inner2 + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + let _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} } - doesEscape { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + doesEscape { //expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} return 0 } takesNoEscapeClosure { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } // okay return 0 } + // Implicit 'self' should be accepted when 'self' has value semantics. struct Outer { @discardableResult func bar() -> Int { bar() func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } @@ -158,17 +159,17 @@ class SomeClass { bar() // no-warning func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{26-26=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{32-32=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } @@ -194,10 +195,7 @@ func testAutoclosure(_ a : @autoclosure () -> Int) { // expected-note{{parameter // QoI: @autoclosure implies @noescape, so you shouldn't be allowed to specify both -func redundant(_ fn : @noescape - @autoclosure () -> Int) { - // expected-error@-2{{unknown attribute 'noescape'}} -} +func redundant(_ fn : @noescape @autoclosure () -> Int) {} // expected-error {{unknown attribute 'noescape'}} protocol P1 { @@ -214,9 +212,7 @@ func overloadedEach(_ source: P, _ transform: @escaping (P.Element) -> struct S : P2 { typealias Element = Int func each(_ transform: @noescape (Int) -> ()) { // expected-error{{unknown attribute 'noescape'}} - // expected-note@-1 {{parameter 'transform' is implicitly non-escaping}} overloadedEach(self, transform, 1) - // expected-error@-1 {{passing non-escaping parameter 'transform' to function expecting an @escaping closure}} } } @@ -260,22 +256,22 @@ public func XCTAssert(_ expression: @autoclosure () -> Bool, _ message: String = /// SR-770 - Currying and `noescape`/`rethrows` don't work together anymore -func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} +func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 1{{unknown attribute 'noescape'}} return { f in x.flatMap(f) } } -func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} - return { (f : @noescape (A) -> [B]) in // expected-error{{unknown attribute 'noescape'}} +func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error {{unknown attribute 'noescape'}} + return { (f : @noescape (A) -> [B]) in x.flatMap(f) } } func bad(_ a : @escaping (Int)-> Int) -> Int { return 42 } func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int { // expected-error{{unknown attribute 'noescape'}} - return { f in // expected-note {{parameter 'f' is implicitly non-escaping}} - bad(f) // expected-error {{passing non-escaping parameter 'f' to function expecting an @escaping closure}} + return { f in + bad(f) } } @@ -295,16 +291,13 @@ typealias CompletionHandler = (_ success: Bool) -> () var escape : CompletionHandlerNE var escapeOther : CompletionHandler func doThing1(_ completion: (_ success: Bool) -> ()) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing2(_ completion: CompletionHandlerNE) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing3(_ completion: CompletionHandler) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing4(_ completion: @escaping CompletionHandler) { escapeOther = completion @@ -312,7 +305,7 @@ func doThing4(_ completion: @escaping CompletionHandler) { // @noescape doesn't work on parameters of function type func apply(_ f: @noescape (T) -> U, g: @noescape (@noescape (T) -> U) -> U) -> U { - // expected-error@-1 6{{unknown attribute 'noescape'}} + // expected-error@-1 2{{unknown attribute 'noescape'}} return g(f) } @@ -322,7 +315,7 @@ enum r19997577Type { case Function(() -> r19997577Type, () -> r19997577Type) case Sum(() -> r19997577Type, () -> r19997577Type) - func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 2{{unknown attribute 'noescape'}} + func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 1{{unknown attribute 'noescape'}} let binary: @noescape (r19997577Type, r19997577Type) -> Result = { combine(combine(combine(initial, self), $0), $1) } // expected-error{{unknown attribute 'noescape'}} switch self { case .Unit: diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index ff125d501f35f..fb87402898c3b 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -1534,7 +1534,7 @@ class infer_instanceVar2< } class infer_instanceVar3 : Class_ObjC1 { -// CHECK-LABEL: @objc class infer_instanceVar3 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_instanceVar3 : Class_ObjC1 { var v1: Int = 0 // CHECK-LABEL: @objc @_hasInitialValue var v1: Int @@ -1599,13 +1599,13 @@ protocol infer_throughConformanceProto1 { } class infer_class1 : PlainClass {} -// CHECK-LABEL: {{^}}class infer_class1 : PlainClass { +// CHECK-LABEL: {{^}}@_inheritsConvenienceInitializers class infer_class1 : PlainClass { class infer_class2 : Class_ObjC1 {} -// CHECK-LABEL: @objc class infer_class2 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class2 : Class_ObjC1 { class infer_class3 : infer_class2 {} -// CHECK-LABEL: @objc class infer_class3 : infer_class2 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class3 : infer_class2 { class infer_class4 : Protocol_Class1 {} // CHECK-LABEL: {{^}}class infer_class4 : Protocol_Class1 { diff --git a/test/attr/attr_originally_definedin_backward_compatibility.swift b/test/attr/attr_originally_definedin_backward_compatibility.swift index 4f79f63b641f7..c93dac9bbce2b 100644 --- a/test/attr/attr_originally_definedin_backward_compatibility.swift +++ b/test/attr/attr_originally_definedin_backward_compatibility.swift @@ -11,7 +11,7 @@ // RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/HighLevelOriginal.swift -Xlinker -install_name -Xlinker @rpath/HighLevel.framework/HighLevel +// RUN: %S/Inputs/SymbolMove/HighLevelOriginal.swift -Xlinker -install_name -Xlinker @rpath/HighLevel.framework/HighLevel -enable-library-evolution // --- Build an executable using the original high level framework // RUN: %target-build-swift -emit-executable %s -g -o %t/HighlevelRunner -F %t/SDK/Frameworks/ -framework HighLevel \ @@ -24,13 +24,13 @@ // RUN: mkdir -p %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/LowLevel.framework/LowLevel) -module-name LowLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/LowLevel.swift -Xlinker -install_name -Xlinker @rpath/LowLevel.framework/LowLevel +// RUN: %S/Inputs/SymbolMove/LowLevel.swift -Xlinker -install_name -Xlinker @rpath/LowLevel.framework/LowLevel -enable-library-evolution // --- Build high level framework. // RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule // RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ // RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ -// RUN: %S/Inputs/SymbolMove/HighLevel.swift -F %t/SDK/Frameworks -Xlinker -reexport_framework -Xlinker LowLevel +// RUN: %S/Inputs/SymbolMove/HighLevel.swift -F %t/SDK/Frameworks -Xlinker -reexport_framework -Xlinker LowLevel -enable-library-evolution // --- Run the executable // RUN: %t/HighlevelRunner | %FileCheck %s -check-prefix=AFTER_MOVE diff --git a/test/decl/circularity.swift b/test/decl/circularity.swift index 69731c3725c5c..fedb35a291b9a 100644 --- a/test/decl/circularity.swift +++ b/test/decl/circularity.swift @@ -96,7 +96,7 @@ class C4 { required init(x: Int) {} } -class D4 : C4, P1 { // expected-note {{through reference here}} +class D4 : C4, P1 { // expected-note 2 {{through reference here}} required init(x: X) { // expected-error {{circular reference}} // expected-note@-1 2{{through reference here}} super.init(x: x) diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index 0e0ceba0194b4..fb53f95a29f27 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -1,19 +1,19 @@ // RUN: rm -rf %t/stats-dir // RUN: mkdir -p %t/stats-dir // RUN: %target-typecheck-verify-swift -// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles +// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles // RUN: %FileCheck %s < %t.cycles // RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot // Check that we produced superclass type requests. // RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 18' %t/stats-dir -class Left // expected-error {{circular reference}} +class Left // expected-error {{circular reference}} expected-note {{through reference here}} : Right.Hand { // expected-note {{through reference here}} class Hand {} } -class Right // expected-note {{through reference here}} +class Right // expected-note 2 {{through reference here}} : Left.Hand { // expected-note {{through reference here}} class Hand {} } @@ -35,13 +35,13 @@ class Outer { class Inner : Outer {} } -class Outer2 // expected-error {{circular reference}} +class Outer2 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer2.Inner { // expected-note {{through reference here}} class Inner {} } -class Outer3 // expected-error {{circular reference}} +class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer3.Inner { // expected-note {{through reference here}} class Inner {} } @@ -79,6 +79,7 @@ class WithDesignatedInit : WithDesignatedInit { // expected-error@-1 {{'WithDesignatedInit' inherits from itself}} // expected-error@-2 {{circular reference}} // expected-note@-3 {{through reference here}} + // expected-note@-4 2 {{through reference here}} init(x: Int) {} // expected-error {{circular reference}} } diff --git a/test/decl/protocol/conforms/fixit_stub_editor.swift b/test/decl/protocol/conforms/fixit_stub_editor.swift index 8b4cb5232405c..30c1aeb2aa1b5 100644 --- a/test/decl/protocol/conforms/fixit_stub_editor.swift +++ b/test/decl/protocol/conforms/fixit_stub_editor.swift @@ -72,3 +72,12 @@ struct Struct3: PropertyMutabilityProto { // expected-error{{type 'Struct3' does class Class4 {} extension Class4: PropertyMutabilityProto { // expected-error{{type 'Class4' does not conform to protocol 'PropertyMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{44-44=\n var computed: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n var stored: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} } + +// https://bugs.swift.org/browse/SR-9868 +protocol FooProto { + typealias CompletionType = (Int) -> Void + func doSomething(then completion: @escaping CompletionType) +} + +struct FooType : FooProto { // expected-error {{type 'FooType' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} {{28-28=\n func doSomething(then completion: @escaping CompletionType) {\n <#code#>\n \}\n}} +} diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index 0bdede947e547..f0b6bf4977bc6 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -118,7 +118,7 @@ struct Circle { func testCircular(_ circle: Circle) { // FIXME: It would be nice if this failure were suppressed because the protocols // have circular definitions. - _ = circle as CircleStart // expected-error{{'Circle' is not convertible to 'CircleStart'; did you mean to use 'as!' to force downcast?}} {{14-16=as!}} + _ = circle as CircleStart // expected-error{{value of type 'Circle' does not conform to 'CircleStart' in coercion}} } // @@ -482,7 +482,7 @@ func f(_ x : T) { class C2 {} func g(_ x : T) { - x as P2 // expected-error{{'T' is not convertible to 'P2'; did you mean to use 'as!' to force downcast?}} {{5-7=as!}} + x as P2 // expected-error{{value of type 'T' does not conform to 'P2' in coercion}} } class C3 : P1 {} // expected-error{{type 'C3' does not conform to protocol 'P1'}} diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 8c236d323ba69..c7b92dc292d9c 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -349,12 +349,14 @@ func testUnresolvedMemberSubscriptFixit(_ s0: GenSubscriptFixitTest) { struct SubscriptTest1 { subscript(keyword:String) -> Bool { return true } - // expected-note@-1 3 {{found this candidate}} expected-note@-1 {{found candidate with type 'Bool'}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'Bool'}} subscript(keyword:String) -> String? {return nil } - // expected-note@-1 3 {{found this candidate}} expected-note@-1 {{found candidate with type 'String?'}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'String?'}} subscript(arg: SubClass) -> Bool { return true } // expected-note {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: Protocol) -> Bool { return true } // expected-note 2 {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: (foo: Bool, bar: (Int, baz: SubClass)), arg2: String) -> Bool { return true } // expected-note@-1 3 {{declared here}} @@ -379,11 +381,9 @@ func testSubscript1(_ s1 : SubscriptTest1) { _ = s1.subscript(ClassConformingToRefinedProtocol()) // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{9-10=}} {{10-19=}} {{19-20=[}} {{54-55=]}} _ = s1.subscript(true) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(Bool)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript(SuperClass()) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(SuperClass)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript("hello") // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} _ = s1.subscript("hello" @@ -398,12 +398,13 @@ func testSubscript1(_ s1 : SubscriptTest1) { struct SubscriptTest2 { subscript(a : String, b : Int) -> Int { return 0 } // expected-note {{candidate expects value of type 'Int' for parameter #2}} // expected-note@-1 {{declared here}} + // expected-note@-2 {{candidate has partially matching parameter list (String, Int)}} subscript(a : String, b : String) -> Int { return 0 } // expected-note {{candidate expects value of type 'String' for parameter #2}} + // expected-note@-1 {{candidate has partially matching parameter list (String, String)}} } func testSubscript1(_ s2 : SubscriptTest2) { - _ = s2["foo"] // expected-error {{cannot subscript a value of type 'SubscriptTest2' with an argument of type 'String'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: (String, Int), (String, String)}} + _ = s2["foo"] // expected-error {{no exact matches in call to subscript}} let a = s2["foo", 1.0] // expected-error {{no exact matches in call to subscript}} diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 78480954fe3f1..914652b69d83d 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -9,12 +9,11 @@ struct Wrapper { init(stored: T) { self._stored = stored } - + var wrappedValue: T { get { _stored } set { _stored = newValue } } - } @propertyWrapper @@ -837,6 +836,42 @@ struct UsesExplicitClosures { var y: Int } +// --------------------------------------------------------------------------- +// Enclosing instance diagnostics +// --------------------------------------------------------------------------- +@propertyWrapper +struct Observable { + private var stored: Value + + init(wrappedValue: Value) { + self.stored = wrappedValue + } + + @available(*, unavailable, message: "must be in a class") + var wrappedValue: Value { // expected-note{{'wrappedValue' has been explicitly marked unavailable here}} + get { fatalError("called wrappedValue getter") } + set { fatalError("called wrappedValue setter") } + } + + static subscript( + _enclosingInstance observed: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + observed[keyPath: storageKeyPath].stored + } + set { + observed[keyPath: storageKeyPath].stored = newValue + } + } +} + +struct MyObservedValueType { + @Observable // expected-error{{'wrappedValue' is unavailable: must be in a class}} + var observedProperty = 17 +} + // --------------------------------------------------------------------------- // Miscellaneous bugs // --------------------------------------------------------------------------- diff --git a/test/decl/var/property_wrappers_invalid.swift b/test/decl/var/property_wrappers_invalid.swift new file mode 100644 index 0000000000000..61fb9811289c6 --- /dev/null +++ b/test/decl/var/property_wrappers_invalid.swift @@ -0,0 +1,36 @@ +// RUN: %target-swift-frontend -typecheck %s -verify -verify-ignore-unknown + +// FIXME: This should produce a diagnostic with a proper +// source location. Right now, we just get three useless errors: + +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context + +// The actual problem is the type of the subscript declaration is wrong. + +public class Store { + @Published public var state: Any + init() { + self.state = 0 + } +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get { fatalError() } + set {} + } + public static subscript(_enclosingInstance object: Any, + wrapped wrappedKeyPath: Any, + storage storageKeyPath: Any) + -> Value { + get { fatalError() } + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get { fatalError() } + } +} diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index f3a81c9836d66..0acd1a1bec08e 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -96,7 +96,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) { _ = i+j // QoI: type variable reconstruction failing for tuple types - let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple type '(x: Int, a: Int)'}} + let (x: g1, a: h1) = (b: x, a: y) // expected-error {{cannot convert value of type '(b: Int, a: Int)' to specified type '(x: Int, a: Int)'}} } // Crash while compiling attached test-app. @@ -107,6 +107,7 @@ func test21057425() -> (Int, Int) { // rdar://problem/21081340 func test21081340() { + func foo() { } let (x: a, y: b): () = foo() // expected-error{{tuple pattern has the wrong length for tuple type '()'}} } diff --git a/test/expr/capture/nested_class.swift b/test/expr/capture/nested_class.swift index 463e99fe7eaf9..2db3a0a154f36 100644 --- a/test/expr/capture/nested_class.swift +++ b/test/expr/capture/nested_class.swift @@ -36,3 +36,20 @@ struct StructWithInnerStruct { } } } + +// Types cannot close over top-level guard bindings +guard let x: Int = nil else { fatalError() } +// expected-note@-1 {{'x' declared here}} + +func getX() -> Int { return x } + +class ClosesOverGuard { // expected-note {{type declared here}} + func foo() { + _ = x + // expected-error@-1 {{class declaration cannot close over value 'x' defined in outer scope}} + } + + func bar() { + _ = getX() // This is diagnosed by SILGen. + } +} diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 845e5bc69ab25..b7621be387d0d 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -151,28 +151,148 @@ func doVoidStuff(_ fn : @escaping () -> ()) {} class ExplicitSelfRequiredTest { var x = 42 func method() -> Int { - // explicit closure requires an explicit "self." base. + // explicit closure requires an explicit "self." base or an explicit capture. doVoidStuff({ self.x += 1 }) - doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{19-19=self.}} + doVoidStuff({ [self] in x += 1 }) + doVoidStuff({ [self = self] in x += 1 }) + doVoidStuff({ [unowned self] in x += 1 }) + doVoidStuff({ [unowned(unsafe) self] in x += 1 }) + doVoidStuff({ [unowned self = self] in x += 1 }) + + doStuff({ [self] in x+1 }) + doStuff({ [self = self] in x+1 }) + doStuff({ self.x+1 }) + doStuff({ [unowned self] in x+1 }) + doStuff({ [unowned(unsafe) self] in x+1 }) + doStuff({ [unowned self = self] in x+1 }) + doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{19-19=self.}} + doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in x += 1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in x+1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + + // Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self." + doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}} + doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}} + doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in _ = method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff { _ = self.method() } + doVoidStuff { [self] in _ = method() } + doVoidStuff { [self = self] in _ = method() } + doVoidStuff({ [unowned self] in _ = method() }) + doVoidStuff({ [unowned(unsafe) self] in _ = method() }) + doVoidStuff({ [unowned self = self] in _ = method() }) - // Methods follow the same rules as properties, uses of 'self' must be marked with "self." - doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} doStuff { self.method() } + doStuff { [self] in method() } + doStuff({ [self = self] in method() }) + doStuff({ [unowned self] in method() }) + doStuff({ [unowned(unsafe) self] in method() }) + doStuff({ [unowned self = self] in method() }) + + // When there's no space between the opening brace and the first expression, insert it + doStuff {method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in }} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} + doVoidStuff {_ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doVoidStuff {() -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{34-34=self.}} + // With an empty capture list, insertion should should be suggested without a comma + doStuff { [] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{21-21=self.}} + doStuff { [ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doStuff { [ /* This space intentionally left blank. */ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{65-65=self.}} + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} + doStuff { [ // Nothing in this capture list! + ] + in + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{9-9=self.}} + } + // An inserted capture list should be on the same line as the opening brace, immediately following it. + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + // Note: Trailing whitespace on the following line is intentional and should not be removed! + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { // We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff {// We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + + // String interpolation should offer the diagnosis and fix-its at the expected locations + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + doVoidStuff { _ = "\(x+1)" } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + + // If we already have a capture list, self should be added to the list + let y = 1 + doStuff { [y] in method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doStuff { [ // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} + y // expected-warning {{capture 'y' was never used}} + ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} // "self." shouldn't be required in the initializer expression in a capture list // This should not produce an error, "x" isn't being captured by the closure. doStuff({ [myX = x] in myX }) // This should produce an error, since x is used within the inner closure. - doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} + doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{23-23= [self] in }} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} // expected-warning @-1 {{capture 'myX' was never used}} return 42 } } +// If the implicit self is of value type, no diagnostic should be produced. +struct ImplicitSelfAllowedInStruct { + var x = 42 + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ x += 1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + +enum ImplicitSelfAllowedInEnum { + case foo + var x: Int { 42 } + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + class SomeClass { var field : SomeClass? @@ -216,7 +336,8 @@ extension SomeClass { doStuff { [weak xyz = self.field] in xyz!.foo() } // rdar://16889886 - Assert when trying to weak capture a property of self in a lazy closure - doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit 'self.' to make capture semantics explicit}} {{36-36=self.}} + // FIXME: We should probably offer a fix-it to the field capture error and suppress the 'implicit self' error. https://bugs.swift.org/browse/SR-11634 + doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} {{36-36=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} // expected-warning @+1 {{variable 'self' was written to, but never read}} doStuff { [weak self&field] in 42 } // expected-error {{expected ']' at end of capture list}} @@ -276,7 +397,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> Bool in }} +let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} if (i > 10) { return true } else { return false } }() @@ -358,7 +479,7 @@ func lvalueCapture(c: GenericClass) { } // Don't expose @lvalue-ness in diagnostics. -let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> Bool in }} +let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} var helper = true return helper } diff --git a/test/expr/postfix/dot/init_ref_delegation.swift b/test/expr/postfix/dot/init_ref_delegation.swift index f9729ef3a8105..33b1512b6ad44 100644 --- a/test/expr/postfix/dot/init_ref_delegation.swift +++ b/test/expr/postfix/dot/init_ref_delegation.swift @@ -323,13 +323,12 @@ class TestOverloadSets { self.init(5, 5) // expected-error{{extra argument in call}} } - convenience init(a : Z0) { - self.init(42 as Int8) // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'TestOverloadSets.init' exist with these partially matching parameter lists: (a: Z0), (value: Double), (value: Int)}} + convenience init(a : Z0) { // expected-note{{candidate has partially matching parameter list (a: Z0)}} + self.init(42 as Int8) // expected-error{{no exact matches in call to initializer}} } - init(value: Int) { /* ... */ } - init(value: Double) { /* ... */ } + init(value: Int) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Int)}} + init(value: Double) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Double)}} } class TestNestedExpr { diff --git a/test/lit.cfg b/test/lit.cfg index a57ad1a3903f0..77b91e355b30f 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -275,6 +275,7 @@ if 'syntax_parser_lib' in config.available_features: config.swift_reflection_dump = inferSwiftBinary('swift-reflection-dump') config.swift_remoteast_test = inferSwiftBinary('swift-remoteast-test') config.swift_indent = inferSwiftBinary('swift-indent') +config.swift_symbolgraph_extract = inferSwiftBinary('swift-symbolgraph-extract') config.clang = inferSwiftBinary('clang') config.llvm_link = inferSwiftBinary('llvm-link') config.swift_llvm_opt = inferSwiftBinary('swift-llvm-opt') @@ -670,6 +671,7 @@ config.substitutions.append(('%target-cpu', run_cpu)) config.substitutions.append(('%target-endian', run_endian)) config.substitutions.append(('%target-os', run_os)) config.substitutions.append(('%target-ptrsize', run_ptrsize)) +config.substitutions.append(('%target-alignment', "%d" % (int(run_ptrsize)/8))) # Enable Darwin SDK-dependent tests if we have an SDK. # On Linux, assume that SDK path does not point to the Darwin SDK. @@ -735,6 +737,7 @@ def use_interpreter_for_simple_runs(): config.available_features.add('interpret') target_specific_module_triple = config.variant_triple +target_future = target_specific_module_triple config.target_run = "" @@ -766,6 +769,7 @@ if run_vendor == 'apple': "-target %s %s %s" % (config.variant_triple, stdlib_resource_dir_opt, mcp_opt)) target_options_for_mock_sdk_after = sdk_overlay_dir_opt + target_future_version = '' if 'arm' in run_cpu and swift_test_mode != 'only_non_executable': raise RuntimeError('Device tests are currently only supported when ' @@ -777,12 +781,15 @@ if run_vendor == 'apple': if run_os == 'ios': lit_config.note('Testing iOS ' + config.variant_triple) xcrun_sdk_name = "iphoneos" + target_future_version = "99.0" elif run_os == 'tvos': lit_config.note('Testing AppleTV ' + config.variant_triple) xcrun_sdk_name = "appletvos" + target_future_version = "99.0" elif run_os == 'watchos': lit_config.note('Testing watchOS ' + config.variant_triple) xcrun_sdk_name = "watchos" + target_future_version = "9.99.0" config.target_cc_options = ( "-arch %s -m%s-version-min=%s %s" % @@ -808,14 +815,17 @@ if run_vendor == 'apple': config.available_features.add('DARWIN_SIMULATOR=ios') lit_config.note("Testing iOS simulator " + config.variant_triple) xcrun_sdk_name = "iphonesimulator" + target_future_version = "99.0" elif run_os == 'watchos': config.available_features.add('DARWIN_SIMULATOR=watchos') lit_config.note("Testing watchOS simulator " + config.variant_triple) xcrun_sdk_name = "watchsimulator" + target_future_version = "9.99.0" else: config.available_features.add('DARWIN_SIMULATOR=tvos') lit_config.note("Testing AppleTV simulator " + config.variant_triple) xcrun_sdk_name = "appletvsimulator" + target_future_version = "99.0" target_specific_module_triple += "-simulator" @@ -868,6 +878,7 @@ if run_vendor == 'apple': swift_execution_tests_extra_flags, sourcekitd_framework_dir, sourcekitd_framework_dir)) config.target_run = "" + target_future_version = "10.99" if 'interpret' in lit_config.params: use_interpreter_for_simple_runs() @@ -904,6 +915,9 @@ if run_vendor == 'apple': config.target_sil_opt = ( "%s %s %s %s" % (xcrun_prefix, config.sil_opt, target_options, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + "%s %s %s" % + (xcrun_prefix, config.swift_symbolgraph_extract, target_options)) config.target_swift_ide_test = ( "%s %s %s %s" % (xcrun_prefix, config.swift_ide_test, target_options, ccp_opt)) @@ -925,6 +939,8 @@ if run_vendor == 'apple': % (config.target_build_swift)) config.target_add_rpath = r'-Xlinker -rpath -Xlinker \1' + target_future = format('%s-apple-%s%s' % (run_cpu, run_os, target_future_version)) + elif run_os in ['windows-msvc']: lit_config.note('Testing Windows ' + config.variant_triple) config.environment['NUMBER_OF_PROCESSORS'] = os.environ['NUMBER_OF_PROCESSORS'] @@ -975,6 +991,10 @@ elif run_os in ['windows-msvc']: ('%r -target %s %s %s %s' % (config.sil_opt, config.variant_triple, \ resource_dir_opt, mcp_opt, \ config.sil_test_options)) + config.target_swift_symbolgraph_extract = \ + ('%r -target %s %s' % (config.swift_symbolgraph_extract, \ + config.variant_triple, \ + mcp_opt)) config.target_swift_ide_test = \ ('%r -target %s %s %s %s' % (config.swift_ide_test, \ config.variant_triple, \ @@ -1068,6 +1088,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_sil_opt = ( '%s -target %s %s %s %s' % (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + '%s -target %s %s' % + (config.swift_symbolgraph_extract, config.variant_triple, mcp_opt)) config.target_swift_ide_test = ( '%s -target %s %s %s %s' % (config.swift_ide_test, config.variant_triple, resource_dir_opt, @@ -1185,6 +1208,10 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-target', config.variant_triple, android_include_paths_opt, resource_dir_opt, mcp_opt, config.sil_test_options]) + config.target_swift_symbolgraph_extract = ' '.join([ + config.swift_symbolgraph_extract, + '-target', config.variant_triple, + mcp_opt]) config.target_swift_ide_test = ' '.join([ config.swift_ide_test, '-target', config.variant_triple, @@ -1216,6 +1243,89 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-L%s' % make_path(test_resource_dir, config.target_sdk_name)]) # The Swift interpreter is not available when targeting Android. config.available_features.discard('swift_interpreter') +elif run_os == 'wasi': + tools_directory = pipes.quote(make_path(config.wasi_sdk_path, "bin")) + + if run_cpu == 'wasm32': + lit_config.note("Testing WebAssembly/WASI " + config.variant_triple) + else: + lit_config.fatal("Unknown environment %s %s" % (run_os, run_cpu)) + + config.target_object_format = "wasm" + config.target_shared_library_prefix = 'lib' + config.target_shared_library_suffix = ".a" + config.target_sdk_name = "wasi" + config.target_runtime = "native" + + # Exclude test cases that use objc-interop because clang doesn't support it + # with WebAssembly binary file yet. + testfiles = glob.glob(os.path.join(config.test_source_root, "**", "*.swift")) + + def use_objc_interop(path): + with open(path) as f: + return '-enable-objc-interop' in f.read() + + import fnmatch + def objc_interop_enabled_filenames(path, filename_pat): + matches = [] + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, filename_pat): + filepath = os.path.join(root, filename) + if not use_objc_interop(filepath): continue + matches.append(filename) + return matches + + config.excludes += objc_interop_enabled_filenames(config.test_source_root, "*.swift") + + config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") + + config.target_build_swift = ' '.join([ + '%s', '-target %s', '-static', '-static-executable', + '-Xcc --sysroot=%s', '-Xclang-linker --sysroot=%s', + '-tools-directory %s', + '-toolchain-stdlib-rpath %s', '%s %s %s %s' + ]) % (config.swiftc, config.variant_triple, + config.variant_sdk, config.variant_sdk, + tools_directory, resource_dir_opt, + mcp_opt, config.swift_test_options, + config.swift_driver_test_options, swift_execution_tests_extra_flags) + config.target_codesign = "echo" + config.target_build_swift_dylib = ( + "%s -parse-as-library -emit-library -o '\\1'" + % (config.target_build_swift)) + config.target_add_rpath = '' + config.target_swift_frontend = ( + '%s -frontend -target %s %s %s %s %s ' + % (config.swift, config.variant_triple, resource_dir_opt, mcp_opt, + config.swift_test_options, config.swift_frontend_test_options)) + subst_target_swift_frontend_mock_sdk = config.target_swift_frontend + subst_target_swift_frontend_mock_sdk_after = "" + config.target_run = 'wasmtime' + if 'interpret' in lit_config.params: + use_interpreter_for_simple_runs() + config.target_sil_opt = ( + '%s -target %s %s %s %s' % + (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_ide_test = ( + '%s -target %s %s %s %s' % + (config.swift_ide_test, config.variant_triple, resource_dir_opt, + mcp_opt, ccp_opt)) + subst_target_swift_ide_test_mock_sdk = config.target_swift_ide_test + subst_target_swift_ide_test_mock_sdk_after = "" + config.target_swiftc_driver = ( + "%s -target %s -toolchain-stdlib-rpath %s %s" % + (config.swiftc, config.variant_triple, resource_dir_opt, mcp_opt)) + config.target_swift_modulewrap = ( + '%s -modulewrap -target %s' % + (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) + config.target_clang = ( + "clang++ -target %s %s -fobjc-runtime=ios-5.0" % + (config.variant_triple, clang_mcp_opt)) + config.target_ld = "ld -L%r" % (make_path(test_resource_dir, config.target_sdk_name)) + else: lit_config.fatal("Don't know how to define target_run and " @@ -1227,6 +1337,7 @@ subst_target_swift_frontend_mock_sdk += " -typo-correction-limit 10 " config.substitutions.append(('%module-target-triple', target_specific_module_triple)) +config.substitutions.append(('%module-target-future', target_future)) # Add 'target-sdk-name' as the name for platform-specific directories config.substitutions.append(('%target-sdk-name', config.target_sdk_name)) @@ -1498,21 +1609,21 @@ if not getattr(config, 'target_run_simple_swift', None): config.target_run_simple_swift_parameterized = \ (SubstituteCaptures('%%empty-directory(%%t) && ' '%s %s %%s \\1 -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run))) config.target_run_simple_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_stdlib_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_simple_swiftgyb = ( @@ -1520,7 +1631,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%gyb %%s -o %%t/main.swift && ' '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) @@ -1530,7 +1641,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) @@ -1613,6 +1724,8 @@ config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', swift_version)))) config.substitutions.append(('%target-swift-ide-test', "%s -swift-version %s" % (config.target_swift_ide_test, swift_version))) +config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) + if not hasattr(config, 'target_swift_reflection_test'): config.target_swift_reflection_test = inferSwiftBinary(swift_reflection_test_name) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 69a41da6cc847..72eb316e92ac3 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -41,6 +41,9 @@ config.darwin_xcrun_toolchain = "@SWIFT_DARWIN_XCRUN_TOOLCHAIN@" config.android_ndk_path = "@SWIFT_ANDROID_NDK_PATH@" config.android_ndk_gcc_version = "@SWIFT_ANDROID_NDK_GCC_VERSION@" +# --- WebAssembly --- +config.wasi_sdk_path = "@SWIFT_WASI_SDK_PATH@" + # --- Windows --- msvc_runtime_flags = { 'MultiThreaded': 'MT', diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected new file mode 100644 index 0000000000000..e8eda6da2ee82 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1: S { +return S() +} + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected new file mode 100644 index 0000000000000..6c0e0427aa9f3 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2: Int { +return 2 +} + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected new file mode 100644 index 0000000000000..8e04908653313 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3: Int { +return 5 +} + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected new file mode 100644 index 0000000000000..b85ee87f74713 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4: Int { +return 4 +} + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected new file mode 100644 index 0000000000000..d909eca08cfc0 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + var field5: Int { +return 5 +} +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected new file mode 100644 index 0000000000000..87f15cfc7bbfe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1: Int { +return 2 +} + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected new file mode 100644 index 0000000000000..09b7fb68a5ebe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2: String { +return "2" +} + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected new file mode 100644 index 0000000000000..2702ab0a321d1 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3: String { +return String() +} + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected new file mode 100644 index 0000000000000..2d53bc984c398 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4: Int { +return 4 +} + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected new file mode 100644 index 0000000000000..f31ba4658f4ce --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! { +return 45 +} +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/basic.swift b/test/refactoring/ConvertToComputedProperty/basic.swift new file mode 100644 index 0000000000000..1a9f119290286 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/basic.swift @@ -0,0 +1,47 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + +// RUN: %empty-directory(%t.result) + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=2:3 -end-pos=2:17 > %t.result/L2-3.swift +// RUN: diff -u %S/Outputs/basic/L2-3.swift.expected %t.result/L2-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=3:3 -end-pos=3:19 > %t.result/L3-3.swift +// RUN: diff -u %S/Outputs/basic/L3-3.swift.expected %t.result/L3-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=4:3 -end-pos=4:24 > %t.result/L4-3.swift +// RUN: diff -u %S/Outputs/basic/L4-3.swift.expected %t.result/L4-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=5:3 -end-pos=5:24 > %t.result/L5-3.swift +// RUN: diff -u %S/Outputs/basic/L5-3.swift.expected %t.result/L5-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=6:3 -end-pos=6:19 > %t.result/L6-3.swift +// RUN: diff -u %S/Outputs/basic/L6-3.swift.expected %t.result/L6-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=10:3 -end-pos=10:26 > %t.result/L10-3.swift +// RUN: diff -u %S/Outputs/basic/L10-3.swift.expected %t.result/L10-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=11:3 -end-pos=11:24 > %t.result/L11-3.swift +// RUN: diff -u %S/Outputs/basic/L11-3.swift.expected %t.result/L11-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=12:3 -end-pos=12:33 > %t.result/L12-3.swift +// RUN: diff -u %S/Outputs/basic/L12-3.swift.expected %t.result/L12-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=13:3 -end-pos=13:67 > %t.result/L13-3.swift +// RUN: diff -u %S/Outputs/basic/L13-3.swift.expected %t.result/L13-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=14:3 -end-pos=14:17 > %t.result/L14-3.swift +// RUN: diff -u %S/Outputs/basic/L14-3.swift.expected %t.result/L14-3.swift diff --git a/test/refactoring/RefactoringKind/basic.swift b/test/refactoring/RefactoringKind/basic.swift index 7d0775c976909..2ec7e032ef8e0 100644 --- a/test/refactoring/RefactoringKind/basic.swift +++ b/test/refactoring/RefactoringKind/basic.swift @@ -275,13 +275,33 @@ func testConvertToIfLetExpr(idxOpt: Int?) { print(idx) } +@propertyWrapper +struct TwelveOrLess { + private var number = 0 + var wrappedValue: Int { + get { return number } + set { number = min(newValue, 12) } + } +} + +struct S { + var field = 2 + let (x, y) = (2, 4) + @TwelveOrLess var height: Int + lazy var z = 42 + var totalSteps: Int = 0 { + willSet(newTotalSteps) { + print("About to set totalSteps to \(newTotalSteps)") + } + } +} // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=3:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=4:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=5:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 -// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK2 +// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=3:16 | %FileCheck %s -check-prefix=CHECK2 // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=4:26 | %FileCheck %s -check-prefix=CHECK2 @@ -367,12 +387,16 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // RUN: %refactor -source-filename %s -pos=251:3 -end-pos=251:24 | %FileCheck %s -check-prefix=CHECK-EXPAND-TERNARY-EXPRESSEXPRESSION -// RUN: %refactor -source-filename %s -pos=257:3 -end-pos=262:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-TERNARY-EXPRESSEXPRESSION - // RUN: %refactor -source-filename %s -pos=266:3 -end-pos=268:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-GUARD-EXPRESSION // RUN: %refactor -source-filename %s -pos=272:3 -end-pos=275:13 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-IFLET-EXPRESSION +// RUN: %refactor -source-filename %s -pos=288:3 -end-pos=288:16 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=289:3 -end-pos=289:22 | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %refactor -source-filename %s -pos=290:3 -end-pos=290:32 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=291:3 -end-pos=291:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=292:3 -end-pos=296:4 | %FileCheck %s -check-prefix=CHECK-NONE + // CHECK1: Action begins // CHECK1-NEXT: Extract Method // CHECK1-NEXT: Action ends @@ -423,3 +447,9 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // CHECK-CONVERT-TO-GUARD-EXPRESSION: Convert To Guard Expression // CHECK-CONVERT-TO-IFLET-EXPRESSION: Convert To IfLet Expression + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY: Convert To Computed Property + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2: Action begins +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Move To Extension +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Action ends \ No newline at end of file diff --git a/test/stdlib/Accelerate.swift b/test/stdlib/Accelerate.swift index 9f19817c9a7b7..7211442e50cec 100644 --- a/test/stdlib/Accelerate.swift +++ b/test/stdlib/Accelerate.swift @@ -529,51 +529,51 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(result, legacyResult)) expectTrue(elementsAlmostEqual(result, returnedResult)) } - - //===----------------------------------------------------------------------===// - // - // Array almost equal. - // - //===----------------------------------------------------------------------===// - - func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { - var returnValue = true - zip(lhs, rhs).forEach { - if !isAlmostEqual($0.0, $0.1) { - returnValue = false - return - } - } - return returnValue - } - - func isAlmostEqual(_ lhs: T, - _ rhs: T, - tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { - assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") - guard lhs.isFinite && rhs.isFinite else { - return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) - } - let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) - return abs(lhs - rhs) < scale*tolerance - } - - func rescaledAlmostEqual(_ lhs: T, - _ rhs: T, - tolerance: T) -> Bool { - if lhs.isNaN || rhs.isNaN { return false } - if lhs.isInfinite { - if rhs.isInfinite { return lhs == rhs } - let scaledLhs = T(sign: lhs.sign, - exponent: T.greatestFiniteMagnitude.exponent, - significand: 1) - let scaledRhs = T(sign: .plus, - exponent: -1, - significand: rhs) - return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) - } - return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) +} + +//===----------------------------------------------------------------------===// +// +// Array almost equal. +// +//===----------------------------------------------------------------------===// + +func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { + var returnValue = true + zip(lhs, rhs).forEach { + if !isAlmostEqual($0.0, $0.1) { + returnValue = false + return } + } + return returnValue +} + +func isAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { + assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") + guard lhs.isFinite && rhs.isFinite else { + return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) + } + let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) + return abs(lhs - rhs) < scale*tolerance +} + +func rescaledAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T) -> Bool { + if lhs.isNaN || rhs.isNaN { return false } + if lhs.isInfinite { + if rhs.isInfinite { return lhs == rhs } + let scaledLhs = T(sign: lhs.sign, + exponent: T.greatestFiniteMagnitude.exponent, + significand: 1) + let scaledRhs = T(sign: .plus, + exponent: -1, + significand: rhs) + return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) + } + return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) } runAllTests() diff --git a/test/stdlib/BridgeIdAsAny.swift.gyb b/test/stdlib/BridgeIdAsAny.swift.gyb index 0caa12107f07b..e0433b026d65c 100644 --- a/test/stdlib/BridgeIdAsAny.swift.gyb +++ b/test/stdlib/BridgeIdAsAny.swift.gyb @@ -175,10 +175,7 @@ protocol P {} %{ testCases = [ # testName type valueExpr testFunc conformsToError conformsToHashable - - # disabled to unblock CI: rdar://problem/57393991 - # ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True), - + ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True), ("strings", "String", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True), ("unbridged type", "KnownUnbridged", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", False, True), ("tuple", "(Int, String)", '(1, "2")', "tupleCanBeDynamicallyCast", False, False), @@ -199,6 +196,11 @@ testCases = [ ] }% +/// Whether this can be safely casted to NSObject +func isNSObject(_ value: T) -> Bool { + return (value is NSObject) && !(value is LifetimeTracked) +} + % for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases: BridgeAnything.test("${testName}") { autoreleasepool { @@ -210,7 +212,7 @@ BridgeAnything.test("${testName}") { let xInArray = [x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) } @@ -219,7 +221,7 @@ BridgeAnything.test("${testName}") { let xInDictValue = ["key" : x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!) } @@ -231,7 +233,7 @@ BridgeAnything.test("${testName}") { // The NSObject version below can't test class LifetimeTracked. // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!) // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!) } diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift index f7b666b6df1f7..da284173bef6f 100644 --- a/test/stdlib/ErrorBridged.swift +++ b/test/stdlib/ErrorBridged.swift @@ -785,6 +785,11 @@ ErrorBridgingTests.test("error-to-NSObject casts") { // "is" check expectTrue(error is NSObject) + + // Unconditional cast to a dictionary. + let dict = ["key" : NoisyError()] + let anyOfDict = dict as AnyObject + let dict2 = anyOfDict as! [String: NSObject] } } @@ -815,4 +820,20 @@ ErrorBridgingTests.test("CFError-to-Error casts") { } } +enum MyError: Error { + case someThing +} + +ErrorBridgingTests.test("SR-9207 crash in failed cast to NSError") { + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + let error = MyError.someThing + let foundationError = error as NSError + + if let urlError = foundationError as? URLError { + expectUnreachable() + } + } +} + runAllTests() diff --git a/test/stdlib/Inputs/CommonArrayTests.gyb b/test/stdlib/Inputs/CommonArrayTests.gyb index bd9d22118b165..20924efb2f138 100644 --- a/test/stdlib/Inputs/CommonArrayTests.gyb +++ b/test/stdlib/Inputs/CommonArrayTests.gyb @@ -106,12 +106,36 @@ ${Suite}.test("${ArrayType}/appendNonUnique") var x: ${ArrayType} = [] x.reserveCapacity(10002) let capacity = x.capacity - for _ in 1...10000 { + for _ in 1...100 { let y = x x.append(1) expectTrue(x.capacity == capacity) - let z = x - x.remove(at: 0) + } +} + +% if ArrayType != 'ArraySlice': +${Suite}.test("${ArrayType}/removeNonUnique") + .code { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x.remove(at: 0) + expectTrue(x.capacity < 1000) + } + } +} +% end + +${Suite}.test("${ArrayType}/mutateNonUnique") + .code { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x[0] = 0 + expectTrue(x.capacity < 1000) } } diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index b2b09fe97a1f2..d3075ca6be337 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -509,6 +509,234 @@ mirrors.test("struct/WrapNSArray") { #endif // _runtime(_ObjC) +//===--- Weak and Unowned References --------------------------------------===// + +// Check that Mirror correctly reflects weak/unowned refs to both +// Swift and ObjC objects from Swift structs and classes. + +protocol WeakUnownedTestsP1: class { + func f1() -> Int +} + +protocol WeakUnownedTestsP2 { + func f2() -> String +} + +class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} + +#if _runtime(_ObjC) +@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} +#endif + +// The four tests below populate objects with different types +// but identical overall structure. +// This function is used by all four to verify that the resulting +// Mirror objects have the expected entries. +func verifyWeakUnownedReflection + + (_ m: Mirror, expectedClass: ExpectedClass.Type ) +{ + let i = m.children.makeIterator() + + func verifyClassField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + } + + func verifyExistentialField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + expectNotNil(child.value) + + // FIXME: These casts are currently broken (Dec 2019) + // Once they are fixed, enable additional checks: + //let vp1 = child.value as? WeakUnownedTestsP1 + //expectNotNil(vp1) + //expectEqual(vp1!.f1(), 2) + //let vp2 = child.value as? WeakUnownedTestsP2 + //expectNotNil(vp2) + //expectEqual(vp2!.f2(), "b") + + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + let m = Mirror(reflecting: v!) + expectEqual(m.displayStyle, .`class`) + // TODO: Find a way to verify that the existential wrapper carries + // the expected protocol witnesses. The current Swift runtime does + // a very good job of hiding this from users. + } + + verifyClassField(child: i.next()!, name: "strong_class") + verifyExistentialField(child: i.next()!, name: "strong_existential") + verifyClassField(child: i.next()!, name: "weak_class") + verifyExistentialField(child: i.next()!, name: "weak_existential") + verifyClassField(child: i.next()!, name: "unowned_safe_class") + verifyExistentialField(child: i.next()!, name: "unowned_safe_existential") + + verifyClassField(child: i.next()!, name: "unowned_unsafe_class") + verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential") + expectNil(i.next()) + + // The original bug report from SR-5289 crashed when the print() code + // attempted to reflect the contents of an unowned field. + // The tests above _should_ suffice to check this, but let's print everything + // anyway just to be sure. + for c in m.children { + print(c.label ?? "?", c.value) + } +} + +#if _runtime(_ObjC) +// Related: SR-5289 reported a crash when using Mirror to inspect Swift +// class objects containing unowned pointers to Obj-C class objects. +mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(objc) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +mirrors.test("Weak and Unowned Obj-C refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(objc) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +#endif + +mirrors.test("Weak and Unowned Swift refs in class") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(swift) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + +mirrors.test("Weak and Unowned Swift refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(swift) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + //===--- Suppressed Superclass Mirrors ------------------------------------===// mirrors.test("Class/Root/NoSuperclassMirror") { class B : CustomReflectable { @@ -854,7 +1082,7 @@ struct GenericStructWithDefaultMirror { mirrors.test("Struct/Generic/DefaultMirror") { do { - var value = GenericStructWithDefaultMirror( + let value = GenericStructWithDefaultMirror( first: 123, second: ["abc", 456, 789.25]) var output = "" @@ -1616,7 +1844,7 @@ mirrors.test("Float") { } do { - var input: Float = 42.125 + let input: Float = 42.125 var output = "" dump(input, to: &output) @@ -1649,7 +1877,7 @@ mirrors.test("Double") { } do { - var input: Double = 42.125 + let input: Double = 42.125 var output = "" dump(input, to: &output) @@ -1745,9 +1973,9 @@ mirrors.test("FieldNamesBug") { } mirrors.test("MirrorMirror") { - var object = 1 - var mirror = Mirror(reflecting: object) - var mirrorMirror = Mirror(reflecting: mirror) + let object = 1 + let mirror = Mirror(reflecting: object) + let mirrorMirror = Mirror(reflecting: mirror) expectEqual(0, mirrorMirror.children.count) } @@ -1755,7 +1983,7 @@ mirrors.test("MirrorMirror") { mirrors.test("OpaquePointer/null") { // Don't crash on null pointers. rdar://problem/19708338 let pointer: OpaquePointer? = nil - let mirror = Mirror(reflecting: pointer) + let mirror = Mirror(reflecting: pointer as Any) expectEqual(0, mirror.children.count) } diff --git a/test/stdlib/NSSetAPI.swift b/test/stdlib/NSSetAPI.swift index 182d976d6d27b..39fdfc64ac5fb 100644 --- a/test/stdlib/NSSetAPI.swift +++ b/test/stdlib/NSSetAPI.swift @@ -67,6 +67,12 @@ NSSetAPI.test("AnyHashable containing NSSet that contains an NSSet") { } } +NSSetAPI.test("Incorrectly constructed Set for backwards compatibility") { + let array:NSArray = [NSObject()] as NSArray + let wrongSet = Set(_immutableCocoaSet: array) + print(wrongSet.startIndex) +} + var NSOrderedSetAPI = TestSuite("NSOrderedSetAPI") NSOrderedSetAPI.test("Sequence") { diff --git a/test/stdlib/TestData.swift b/test/stdlib/TestData.swift index cb7d36b8fccb1..7608e9b8cace2 100644 --- a/test/stdlib/TestData.swift +++ b/test/stdlib/TestData.swift @@ -3836,6 +3836,53 @@ class TestData : TestDataSuper { } } } + + func test_increaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + var data = Data(initial) + data.count += diff + expectEqualSequence( + Array(initial) + Array(repeating: 0, count: diff), + data) + } + } + } + + func test_decreaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + guard initial.count >= diff else { continue } + var data = Data(initial) + data.count -= diff + expectEqualSequence( + initial.dropLast(diff), + data) + } + } + } } #if !FOUNDATION_XCTEST @@ -4159,6 +4206,9 @@ if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { DataTests.test("test_nsdataSequence") { TestData().test_nsdataSequence() } DataTests.test("test_dispatchSequence") { TestData().test_dispatchSequence() } } +DataTests.test("test_increaseCount") { TestData().test_increaseCount() } +DataTests.test("test_decreaseCount") { TestData().test_decreaseCount() } + // XCTest does not have a crash detection, whereas lit does DataTests.test("bounding failure subdata") { diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 630840455ef29..2082be1455aa6 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -133,14 +133,14 @@ func testForEachInference() { // Generic sequence resolved contextually for i: Int in getGenericSeq() { } for d: Double in getGenericSeq() { } - + // Inference of generic arguments in the element type from the // sequence. - for x: X in getXIntSeq() { + for x: X in getXIntSeq() { let z = x.value + 1 } - for x: X in getOvlSeq() { + for x: X in getOvlSeq() { let z = x.value + 1 } diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 2536568f3aae5..4714a0003b2e0 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -235,11 +235,23 @@ func DoStmt() { } } -func DoWhileStmt() { - do { // expected-error {{'do-while' statement is not allowed; use 'repeat-while' instead}} {{3-5=repeat}} + +func DoWhileStmt1() { + do { // expected-error {{'do-while' statement is not allowed}} + // expected-note@-1 {{did you mean 'repeat-while' statement?}} {{3-5=repeat}} + // expected-note@-2 {{did you mean separate 'do' and 'while' statements?}} {{5-5=\n}} } while true } +func DoWhileStmt2() { + do { + + } + while true { + + } +} + //===--- Repeat-while statement. func RepeatWhileStmt1() { diff --git a/test/type/subclass_composition.swift b/test/type/subclass_composition.swift index 69ed053d87cb5..b9567ef0b3b36 100644 --- a/test/type/subclass_composition.swift +++ b/test/type/subclass_composition.swift @@ -105,8 +105,7 @@ func basicSubtyping( let _: Derived = baseAndP2 // expected-error {{cannot convert value of type 'Base & P2' to specified type 'Derived'}} let _: Derived & P2 = baseAndP2 // expected-error {{value of type 'Base & P2' does not conform to specified type 'Derived & P2'}} - // TODO(diagnostics): Diagnostic regression, better message is `value of type 'Unrelated' does not conform to 'Derived & P2' in coercion` - let _ = Unrelated() as Derived & P2 // expected-error {{cannot convert value of type 'Unrelated' to type 'Derived' in coercion}} + let _ = Unrelated() as Derived & P2 // expected-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}} let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}} let _ = baseAndP2 as Unrelated // expected-error {{cannot convert value of type 'Base & P2' to type 'Unrelated' in coercion}} let _ = baseAndP2 as? Unrelated // expected-warning {{always fails}} diff --git a/test/type/types.swift b/test/type/types.swift index 47b4b8f5ab689..6ee2cb8dd5922 100644 --- a/test/type/types.swift +++ b/test/type/types.swift @@ -19,8 +19,12 @@ var d3 : () -> Float = { 4 } var d4 : () -> Int = { d2 } // expected-error{{function produces expected type 'Int'; did you mean to call it with '()'?}} {{26-26=()}} var e0 : [Int] -e0[] // expected-error {{cannot subscript a value of type '[Int]' with an argument of type '()'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: ((UnboundedRange_) -> ()), (Int), (R), (Range), (Range)}} +e0[] // expected-error {{no exact matches in call to subscript}} +// expected-note@-1 {{candidate has partially matching parameter list (Int)}} +// expected-note@-2 {{candidate has partially matching parameter list (Range)}} +// expected-note@-3 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} +// expected-note@-4 {{candidate has partially matching parameter list (Range.Index>)}} +// expected-note@-5 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} var f0 : [Float] var f1 : [(Int,Int)] diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index 39e011519c35e..8efd59710305b 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -198,6 +198,13 @@ macro(add_sourcekit_library name) COMPONENT "${SOURCEKITLIB_INSTALL_IN_COMPONENT}") set_target_properties(${name} PROPERTIES FOLDER "SourceKit libraries") add_sourcekit_default_compiler_flags("${name}") + + swift_is_installing_component("${SOURCEKITLIB_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() endmacro() # Add a new SourceKit executable. @@ -324,7 +331,6 @@ macro(add_sourcekit_framework name) endif() endif() - if (SOURCEKIT_DEPLOYMENT_OS MATCHES "^macosx") set_output_directory(${name} BINARY_DIR ${SOURCEKIT_RUNTIME_OUTPUT_INTDIR} @@ -374,6 +380,14 @@ macro(add_sourcekit_framework name) COMMAND ${CMAKE_COMMAND} -E copy "${hdr}" "${framework_location}/Headers/${hdrname}") endforeach() endif() + + swift_is_installing_component("${SOURCEKITFW_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() + add_sourcekit_default_compiler_flags("${name}") endmacro(add_sourcekit_framework) diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index b1737186dcc58..5341716393419 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -653,6 +653,7 @@ class LangSupport { virtual void codeComplete(llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) = 0; @@ -801,13 +802,15 @@ class LangSupport { virtual void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) = 0; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) = 0; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getStatistics(StatisticsReceiver) = 0; }; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 2fc29e21328b1..83635923ba70f 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -58,7 +58,7 @@ class ImportDepth { public: ImportDepth() = default; - ImportDepth(ASTContext &context, CompilerInvocation &invocation); + ImportDepth(ASTContext &context, const CompilerInvocation &invocation); Optional lookup(StringRef module) { auto I = depths.find(module); @@ -320,7 +320,8 @@ CodeCompletionViewRef CodeCompletionOrganizer::takeResultsView() { // ImportDepth //===----------------------------------------------------------------------===// -ImportDepth::ImportDepth(ASTContext &context, CompilerInvocation &invocation) { +ImportDepth::ImportDepth(ASTContext &context, + const CompilerInvocation &invocation) { llvm::DenseSet seen; std::deque> worklist; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h index 9d79f2ea55b6c..27d53585c5af6 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h @@ -42,6 +42,7 @@ struct Options { bool hideLowPriority = true; bool hideByNameStyle = true; bool fuzzyMatching = true; + bool reuseASTContextIfPossible = false; unsigned minFuzzyLength = 2; unsigned showTopNonLiteralResults = 3; @@ -53,7 +54,7 @@ struct Options { struct SwiftCompletionInfo { swift::ASTContext *swiftASTContext = nullptr; - swift::CompilerInvocation *invocation = nullptr; + const swift::CompilerInvocation *invocation = nullptr; swift::ide::CodeCompletionContext *completionContext = nullptr; }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index eb4b1fa48b761..2ca913da9b417 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -22,6 +22,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -90,7 +91,7 @@ struct SwiftCodeCompletionConsumer : handleResultsImpl(handleResultsImpl) {} void setContext(swift::ASTContext *context, - swift::CompilerInvocation *invocation, + const swift::CompilerInvocation *invocation, swift::ide::CodeCompletionContext *completionContext) { swiftContext.swiftASTContext = context; swiftContext.invocation = invocation; @@ -100,7 +101,6 @@ struct SwiftCodeCompletionConsumer void handleResults(MutableArrayRef Results) override { assert(swiftContext.swiftASTContext); - CodeCompletionContext::sortCompletionResults(Results); handleResultsImpl(Results, swiftContext); } }; @@ -123,110 +123,47 @@ static bool swiftCodeCompleteImpl( unsigned Offset, SwiftCodeCompletionConsumer &SwiftConsumer, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { - assert(FileSystem); - - // Resolve symlinks for the input file; we resolve them for the input files - // in the arguments as well. - // FIXME: We need the Swift equivalent of Clang's FileEntry. - auto InputFile = llvm::MemoryBuffer::getMemBuffer( - UnresolvedInputFile->getBuffer(), - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier())); - - auto origBuffSize = InputFile->getBufferSize(); - unsigned CodeCompletionOffset = Offset; - if (CodeCompletionOffset > origBuffSize) { - CodeCompletionOffset = origBuffSize; - } - - CompilerInstance CI; - // Display diagnostics to stderr. - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, InputFile->getBufferIdentifier(), Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start(SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(Offset)), - std::make_pair("Offset", - std::to_string(CodeCompletionOffset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), InputFile->getBufferIdentifier(), - FileSystem, Error); - if (Failed) { - return false; - } - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - const char *Position = InputFile->getBufferStart() + CodeCompletionOffset; - std::unique_ptr NewBuffer = - llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - InputFile->getBufferSize() + 1, - InputFile->getBufferIdentifier()); - char *NewBuf = NewBuffer->getBufferStart(); - char *NewPos = std::copy(InputFile->getBufferStart(), Position, NewBuf); - *NewPos = '\0'; - std::copy(Position, InputFile->getBufferEnd(), NewPos+1); - - Invocation.setCodeCompletionPoint(NewBuffer.get(), CodeCompletionOffset); - - auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. - ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - SwiftConsumer)); - - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); - - // FIXME: We need to be passing the buffers from the open documents. - // It is not a huge problem in practice because Xcode auto-saves constantly. - - if (FileSystem != llvm::vfs::getRealFileSystem()) { - CI.getSourceMgr().setFileSystem(FileSystem); - } - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - - CloseClangModuleFiles scopedCloseFiles( - *CI.getASTContext().getClangModuleLoader()); - SwiftConsumer.setContext(&CI.getASTContext(), &Invocation, - &CompletionContext); - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - SwiftConsumer.clearContext(); - - return true; + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. + ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, + SwiftConsumer)); + + SwiftConsumer.setContext(&CI.getASTContext(), &CI.getInvocation(), + &CompletionContext); + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + SwiftConsumer.clearContext(); + }); } +static void translateCodeCompletionOptions(OptionsDictionary &from, + CodeCompletion::Options &to, + StringRef &filterText, + unsigned &resultOffset, + unsigned &maxResults); + void SwiftLangSupport::codeComplete( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &SKConsumer, ArrayRef Args, Optional vfsOptions) { + CodeCompletion::Options CCOpts; + if (options) { + StringRef filterText; + unsigned resultOffset = 0; + unsigned maxResults = 0; + translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, + maxResults); + } + std::string error; // FIXME: the use of None as primary file is to match the fact we do not read // the document contents using the editor documents infrastructure. @@ -271,7 +208,8 @@ void SwiftLangSupport::codeComplete( std::string Error; if (!swiftCodeCompleteImpl(*this, UnresolvedInputFile, Offset, SwiftConsumer, - Args, fileSystem, Error)) { + Args, fileSystem, + CCOpts.reuseASTContextIfPossible, Error)) { SKConsumer.failed(Error); } } @@ -846,6 +784,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, static UIdent KeyContextWeight("key.codecomplete.sort.contextweight"); static UIdent KeyFuzzyWeight("key.codecomplete.sort.fuzzyweight"); static UIdent KeyPopularityBonus("key.codecomplete.sort.popularitybonus"); + static UIdent KeyReuseASTContext("key.codecomplete.reuseastcontext"); from.valueForOption(KeySortByName, to.sortByName); from.valueForOption(KeyUseImportDepth, to.useImportDepth); from.valueForOption(KeyGroupOverloads, to.groupOverloads); @@ -869,6 +808,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, from.valueForOption(KeyPopularityBonus, to.popularityBonus); from.valueForOption(KeyHideByName, to.hideByNameStyle); from.valueForOption(KeyTopNonLiteral, to.showTopNonLiteralResults); + from.valueForOption(KeyReuseASTContext, to.reuseASTContextIfPossible); } /// Canonicalize a name that is in the format of a reference to a function into @@ -1139,7 +1079,8 @@ static void transformAndForwardResults( cargs.push_back(arg.c_str()); std::string error; if (!swiftCodeCompleteImpl(lang, buffer.get(), str.size(), swiftConsumer, - cargs, session->getFileSystem(), error)) { + cargs, session->getFileSystem(), + options.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } @@ -1178,7 +1119,8 @@ static void transformAndForwardResults( void SwiftLangSupport::codeCompleteOpen( StringRef name, llvm::MemoryBuffer *inputBuf, unsigned offset, OptionsDictionary *options, ArrayRef rawFilterRules, - GroupedCodeCompletionConsumer &consumer, ArrayRef args, Optional vfsOptions) { + GroupedCodeCompletionConsumer &consumer, ArrayRef args, + Optional vfsOptions) { StringRef filterText; unsigned resultOffset = 0; unsigned maxResults = 0; @@ -1236,7 +1178,8 @@ void SwiftLangSupport::codeCompleteOpen( // Invoke completion. if (!swiftCodeCompleteImpl(*this, inputBuf, offset, swiftConsumer, - extendedArgs, fileSystem, error)) { + extendedArgs, fileSystem, + CCOpts.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 375cd44d639e5..eeeb0bc3b76df 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -16,6 +16,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/ConformingMethodList.h" +#include "swift/IDE/CompletionInstance.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" #include "clang/AST/Decl.h" @@ -29,72 +30,35 @@ static bool swiftConformingMethodListImpl( SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - ide::ConformingMethodListConsumer &Consumer, std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); - - auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - CompilerInstance CI; - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, - Consumer)); - - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - - return true; + ide::ConformingMethodListConsumer &Consumer, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, + Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getConformingMethodList( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - SourceKit::ConformingMethodListConsumer &SKConsumer) { + SourceKit::ConformingMethodListConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); class Consumer : public ide::ConformingMethodListConsumer { SourceKit::ConformingMethodListConsumer &SKConsumer; @@ -210,9 +174,9 @@ void SwiftLangSupport::getConformingMethodList( } } Consumer(SKConsumer); - std::string Error; if (!swiftConformingMethodListImpl(*this, UnresolvedInputFile, Offset, Args, - ExpectedTypeNames, Consumer, Error)) { - SKConsumer.failed(Error); + ExpectedTypeNames, Consumer, fileSystem, + /*EnableASTCaching=*/false, error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index a439a4ae14b92..9ed275a1d1a87 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -41,7 +41,7 @@ using namespace swift; using namespace ide; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -52,7 +52,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index a331e2051d61c..475629e4a22db 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -92,7 +92,7 @@ typedef SwiftInterfaceGenContext::Implementation::TextDecl TextDecl; typedef SwiftInterfaceGenContext::Implementation::SourceTextInfo SourceTextInfo; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -103,7 +103,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index 1bd390a5fa96f..fe547e0ec693d 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -12,6 +12,7 @@ #include "SwiftLangSupport.h" #include "SwiftASTManager.h" +#include "SwiftEditorDiagConsumer.h" #include "SourceKit/Core/Context.h" #include "SourceKit/SwiftLang/Factory.h" #include "SourceKit/Support/FileSystemProvider.h" @@ -23,8 +24,10 @@ #include "swift/AST/SILOptions.h" #include "swift/AST/USRGeneration.h" #include "swift/Config.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/Utils.h" @@ -266,6 +269,9 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) ASTMgr = std::make_shared(EditorDocuments, SKCtx.getGlobalConfiguration(), Stats, RuntimeResourcePath); + + CompletionInst = std::make_unique(); + // By default, just use the in-memory cache. CCCache->inMemory = llvm::make_unique(); @@ -276,26 +282,6 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SwiftLangSupport::~SwiftLangSupport() { } -std::unique_ptr -SwiftLangSupport::makeCodeCompletionMemoryBuffer( - const llvm::MemoryBuffer *origBuf, unsigned &Offset, - const std::string bufferIdentifier) { - - auto origBuffSize = origBuf->getBufferSize(); - if (Offset > origBuffSize) - Offset = origBuffSize; - - auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - origBuffSize + 1, bufferIdentifier); - auto *pos = origBuf->getBufferStart() + Offset; - auto *newPos = - std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); - *newPos = '\0'; - std::copy(pos, origBuf->getBufferEnd(), newPos + 1); - - return std::unique_ptr(newBuffer.release()); -} - UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) { return UIdentVisitor(IsRef).visit(const_cast(D)); } @@ -967,6 +953,70 @@ SwiftLangSupport::getFileSystem(const Optional &vfsOptions, return llvm::vfs::getRealFileSystem(); } +bool SwiftLangSupport::performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error, + llvm::function_ref Callback) { + assert(FileSystem); + + // Resolve symlinks for the input file; we resolve them for the input files + // in the arguments as well. + // FIXME: We need the Swift equivalent of Clang's FileEntry. + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); + + // Create a buffer for code completion. This contains '\0' at 'Offset' + // position of 'UnresolvedInputFile' buffer. + auto origOffset = Offset; + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( + UnresolvedInputFile, Offset, bufferIdentifier); + + SourceManager SM; + DiagnosticEngine Diags(SM); + PrintingDiagnosticConsumer PrintDiags; + EditorDiagConsumer TraceDiags; + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); + if (TracedOp.enabled()) { + Diags.addConsumer(TraceDiags); + trace::SwiftInvocation SwiftArgs; + trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); + TracedOp.setDiagnosticProvider( + [&](SmallVectorImpl &diags) { + TraceDiags.getAllDiagnostics(diags); + }); + TracedOp.start( + SwiftArgs, + {std::make_pair("OriginalOffset", std::to_string(origOffset)), + std::make_pair("Offset", std::to_string(Offset))}); + } + ForwardingDiagnosticConsumer CIDiags(Diags); + + CompilerInvocation Invocation; + bool Failed = getASTManager()->initCompilerInvocation( + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); + if (Failed) + return false; + if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { + Error = "no input filenames specified"; + return false; + } + + // Pin completion instance. + auto CompletionInst = getCompletionInstance(); + + return CompletionInst->performOperation(Invocation, Args, FileSystem, + newBuffer.get(), Offset, + EnableASTCaching, Error, + &CIDiags, Callback); +} + CloseClangModuleFiles::~CloseClangModuleFiles() { clang::Preprocessor &PP = loader.getClangPreprocessor(); clang::ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 5096798ab6a6a..2c37911224119 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -50,6 +50,7 @@ namespace syntax { namespace ide { class CodeCompletionCache; + class CompletionInstance; class OnDiskCodeCompletionCache; class SourceEditConsumer; enum class CodeCompletionDeclKind; @@ -294,6 +295,7 @@ class SwiftLangSupport : public LangSupport { ThreadSafeRefCntPtr CustomCompletions; std::shared_ptr Stats; llvm::StringMap> FileSystemProviders; + std::shared_ptr CompletionInst; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -313,6 +315,10 @@ class SwiftLangSupport : public LangSupport { return CCCache; } + std::shared_ptr getCompletionInstance() { + return CompletionInst; + } + /// Returns the FileSystemProvider registered under Name, or nullptr if not /// found. FileSystemProvider *getFileSystemProvider(StringRef Name); @@ -342,13 +348,6 @@ class SwiftLangSupport : public LangSupport { getFileSystem(const Optional &vfsOptions, Optional primaryFile, std::string &error); - /// Copy a memory buffer inserting '0' at the position of \c origBuf. - // TODO: Share with code completion. - static std::unique_ptr - makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, - unsigned &Offset, - const std::string bufferIdentifier); - static SourceKit::UIdent getUIDForDecl(const swift::Decl *D, bool IsRef = false); static SourceKit::UIdent getUIDForExtensionOfDecl(const swift::Decl *D); @@ -437,6 +436,16 @@ class SwiftLangSupport : public LangSupport { /// returns the original path; static std::string resolvePathSymlinks(StringRef FilePath); + /// Perform a completion like operation. It initializes a \c CompilerInstance, + /// the calls \p Callback with it. \p Callback must perform the second pass + /// using that instance. + bool performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error, + llvm::function_ref Callback); + //==========================================================================// // LangSupport Interface //==========================================================================// @@ -446,6 +455,7 @@ class SwiftLangSupport : public LangSupport { void codeComplete( llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) override; @@ -585,12 +595,14 @@ class SwiftLangSupport : public LangSupport { void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) override; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) override; void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) override; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) override; void getStatistics(StatisticsReceiver) override; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 40ababa58c06c..17ae2701f3451 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -15,6 +15,7 @@ #include "SwiftEditorDiagConsumer.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/TypeContextInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" @@ -24,76 +25,38 @@ using namespace SourceKit; using namespace swift; using namespace ide; -static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, - llvm::MemoryBuffer *UnresolvedInputFile, - unsigned Offset, - ArrayRef Args, - ide::TypeContextInfoConsumer &Consumer, - std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); - - auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - CompilerInstance CI; - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeTypeContextInfoCallbacksFactory(Consumer)); - - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - - return true; +static bool swiftTypeContextInfoImpl( + SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, + unsigned Offset, ide::TypeContextInfoConsumer &Consumer, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeTypeContextInfoCallbacksFactory(Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getExpressionContextInfo( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, - SourceKit::TypeContextInfoConsumer &SKConsumer) { + SourceKit::TypeContextInfoConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); + class Consumer : public ide::TypeContextInfoConsumer { SourceKit::TypeContextInfoConsumer &SKConsumer; @@ -187,9 +150,9 @@ void SwiftLangSupport::getExpressionContextInfo( } } Consumer(SKConsumer); - std::string Error; - if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Args, - Consumer, Error)) { - SKConsumer.failed(Error); + if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Consumer, + Args, fileSystem, /*EnableASTCaching=*/false, + error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/tools/CMakeLists.txt b/tools/SourceKit/tools/CMakeLists.txt index 6c89dc8a6c92b..ad651d0d6fb78 100644 --- a/tools/SourceKit/tools/CMakeLists.txt +++ b/tools/SourceKit/tools/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( add_swift_lib_subdirectory(sourcekitd) add_swift_tool_subdirectory(sourcekitd-test) -if(HAVE_UNICODE_LIBEDIT) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) add_swift_tool_subdirectory(sourcekitd-repl) endif() add_swift_tool_subdirectory(complete-test) diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 49c40efe5b9ff..fe2bc449a0ab5 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -1,36 +1,34 @@ -set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} edit) -check_symbol_exists(el_wgets "histedit.h" HAVE_UNICODE_LIBEDIT) - -if(HAVE_UNICODE_LIBEDIT) - add_sourcekit_executable(sourcekitd-repl - sourcekitd-repl.cpp - LLVM_LINK_COMPONENTS coverage lto - ) - target_link_libraries(sourcekitd-repl PRIVATE edit) - if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) - else() - target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) - endif() - if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) - target_link_libraries(sourcekitd-repl PRIVATE - dispatch - BlocksRuntime) - endif() - - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set_target_properties(sourcekitd-repl - PROPERTIES - LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") - endif() - if(SWIFT_ANALYZE_CODE_COVERAGE) - set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY - LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") - endif() +add_sourcekit_executable(sourcekitd-repl + sourcekitd-repl.cpp + LLVM_LINK_COMPONENTS coverage lto +) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) +else() + target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) +endif() +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-repl PRIVATE + dispatch + BlocksRuntime) +endif() +target_include_directories(sourcekitd-repl PRIVATE + ${LibEdit_INCLUDE_DIRS}) +target_link_libraries(sourcekitd-repl PRIVATE + ${LibEdit_LIBRARIES}) - add_dependencies(tools sourcekitd-repl) - swift_install_in_component(TARGETS sourcekitd-repl - RUNTIME - DESTINATION bin - COMPONENT tools) +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set_target_properties(sourcekitd-repl + PROPERTIES + LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") +endif() +if(SWIFT_ANALYZE_CODE_COVERAGE) + set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY + LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") endif() + +add_dependencies(tools sourcekitd-repl) +swift_install_in_component(TARGETS sourcekitd-repl + RUNTIME + DESTINATION bin + COMPONENT tools) diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 6a397dff7ff99..ab2c0683b914f 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -573,6 +573,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::CodeComplete: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCodeComplete); sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); + addCodeCompleteOptions(Req, Opts); break; case SourceKitRequest::CodeCompleteOpen: @@ -970,6 +972,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (Opts.SourceText) { sourcekitd_request_dictionary_set_string(Req, KeySourceText, Opts.SourceText->c_str()); + sourcekitd_request_dictionary_set_string(Req, KeySourceFile, + SemaName.c_str()); } if (!Opts.CompilerArgs.empty()) { diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 66d184e0cf544..ccade323104fe 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -158,6 +158,7 @@ static void findRelatedIdents(StringRef Filename, static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions); static sourcekitd_response_t codeCompleteOpen(StringRef name, @@ -175,12 +176,14 @@ static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset); static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args); + ArrayRef Args, + Optional vfsOptions); static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes); + ArrayRef ExpectedTypes, + Optional vfsOptions); static sourcekitd_response_t editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, @@ -938,7 +941,9 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(codeComplete(InputBuf.get(), Offset, Args, std::move(vfsOptions))); + Optional options = Req.getDictionary(KeyCodeCompleteOptions); + return Rec(codeComplete(InputBuf.get(), Offset, options, Args, + std::move(vfsOptions))); } if (ReqUID == RequestCodeCompleteOpen) { @@ -976,7 +981,8 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(typeContextInfo(InputBuf.get(), Offset, Args)); + return Rec(typeContextInfo(InputBuf.get(), Offset, Args, + std::move(vfsOptions))); } if (ReqUID == RequestConformingMethodList) { @@ -991,7 +997,8 @@ static void handleSemanticRequest( if (Req.getStringArray(KeyExpectedTypes, ExpectedTypeNames, true)) return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'")); return Rec( - conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames)); + conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames, + std::move(vfsOptions))); } if (!SourceFile.hasValue()) @@ -1928,12 +1935,19 @@ class SKCodeCompletionConsumer : public CodeCompletionConsumer { static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions) { ResponseBuilder RespBuilder; SKCodeCompletionConsumer CCC(RespBuilder); + + std::unique_ptr options; + if (optionsDict) + options = std::make_unique(*optionsDict); + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.codeComplete(InputBuf, Offset, CCC, Args, std::move(vfsOptions)); + Lang.codeComplete(InputBuf, Offset, options.get(), CCC, Args, + std::move(vfsOptions)); return CCC.createResponse(); } @@ -2205,7 +2219,8 @@ void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args) { + ArrayRef Args, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public TypeContextInfoConsumer { @@ -2242,7 +2257,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer); + Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); @@ -2256,7 +2272,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes) { + ArrayRef ExpectedTypes, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public ConformingMethodListConsumer { @@ -2293,7 +2310,8 @@ conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer); + Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 10e926b4e67e8..2b5eb14dc0d7a 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -3,15 +3,14 @@ add_swift_host_tool(swift autolink_extract_main.cpp modulewrap_main.cpp swift_indent_main.cpp + swift_symbolgraph_extract_main.cpp SWIFT_COMPONENT compiler ) target_link_libraries(swift PRIVATE swiftDriver - swiftFrontendTool) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift PRIVATE edit) -endif() + swiftFrontendTool + swiftSymbolGraphGen) swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" @@ -23,6 +22,11 @@ swift_create_post_build_symlink(swift DESTINATION "swift-indent${CMAKE_EXECUTABLE_SUFFIX}" WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") +swift_create_post_build_symlink(swift + SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "swift-autolink-extract${CMAKE_EXECUTABLE_SUFFIX}" @@ -31,6 +35,7 @@ swift_create_post_build_symlink(swift add_swift_tool_symlink(swiftc swift compiler) add_swift_tool_symlink(swift-autolink-extract swift autolink-driver) add_swift_tool_symlink(swift-indent swift editor-integration) +add_swift_tool_symlink(swift-symbolgraph-extract swift toolchain-tools) # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) @@ -49,3 +54,7 @@ add_dependencies(editor-integration swift) swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-indent${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "bin" COMPONENT editor-integration) +add_dependencies(toolchain-tools swift) +swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "bin" + COMPONENT toolchain-tools) diff --git a/tools/driver/autolink_extract_main.cpp b/tools/driver/autolink_extract_main.cpp index f805714c30eb2..dfc70b9941f5d 100644 --- a/tools/driver/autolink_extract_main.cpp +++ b/tools/driver/autolink_extract_main.cpp @@ -32,6 +32,8 @@ #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/Wasm.h" +#include "llvm/BinaryFormat/Wasm.h" using namespace swift; using namespace llvm::opt; @@ -140,6 +142,31 @@ extractLinkerFlagsFromObjectFile(const llvm::object::ObjectFile *ObjectFile, return false; } +/// Look inside the object file 'WasmObjectFile' and append any linker flags found in +/// its ".swift1_autolink_entries" section to 'LinkerFlags'. +/// Return 'true' if there was an error, and 'false' otherwise. +static bool +extractLinkerFlagsFromObjectFile(const llvm::object::WasmObjectFile *ObjectFile, + std::vector &LinkerFlags, + CompilerInstance &Instance) { + + // Search for the data segment we hold autolink entries in + for (const llvm::object::WasmSegment &Segment : ObjectFile->dataSegments()) { + if (Segment.Data.Name == ".swift1_autolink_entries") { + + StringRef SegmentData = llvm::toStringRef(Segment.Data.Content); + // entries are null-terminated, so extract them and push them into + // the set. + llvm::SmallVector SplitFlags; + SegmentData.split(SplitFlags, llvm::StringRef("\0", 1), -1, + /*KeepEmpty=*/false); + for (const auto &Flag : SplitFlags) + LinkerFlags.push_back(Flag); + } + } + return false; +} + /// Look inside the binary 'Bin' and append any linker flags found in its /// ".swift1_autolink_entries" section to 'LinkerFlags'. If 'Bin' is an archive, /// recursively look inside all children within the archive. Return 'true' if @@ -150,6 +177,8 @@ static bool extractLinkerFlags(const llvm::object::Binary *Bin, std::vector &LinkerFlags) { if (auto *ObjectFile = llvm::dyn_cast(Bin)) { return extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags, Instance); + } else if (auto *ObjectFile = llvm::dyn_cast(Bin)) { + return extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags, Instance); } else if (auto *Archive = llvm::dyn_cast(Bin)) { llvm::Error Error = llvm::Error::success(); for (const auto &Child : Archive->children(Error)) { diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0424977e4012a..17a451c76a051 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -70,6 +70,10 @@ extern int modulewrap_main(ArrayRef Args, const char *Argv0, extern int swift_indent_main(ArrayRef Args, const char *Argv0, void *MainAddr); +/// Run 'swift-symbolgraph-extract' +extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, +void *MainAddr); + /// Determine if the given invocation should run as a subcommand. /// /// \param ExecName The name of the argv[0] we were invoked as. @@ -152,6 +156,8 @@ static int run_driver(StringRef ExecName, return swift_indent_main( TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SymbolGraph: + return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); default: break; } diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp new file mode 100644 index 0000000000000..ef01acf5c3e58 --- /dev/null +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -0,0 +1,177 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Extracts a Symbol Graph from a .swiftmodule file. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/Version.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" + +using namespace swift; +using namespace llvm::opt; + +namespace options { +static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options"); + +static llvm::cl::opt +ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category)); + +static llvm::cl::list +FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::opt +ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category)); + +static llvm::cl::opt +SDK("sdk", llvm::cl::desc("Path to the SDK"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +Target("target", llvm::cl::desc("Target triple"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category)); + +static llvm::cl::opt +PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category)); + +static llvm::cl::opt +MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); + +static llvm::cl::opt +OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); +} // end namespace options + +int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, void *MainAddr) { + INITIALIZE_LLVM(); + + llvm::cl::HideUnrelatedOptions(options::Category); + + // LLVM Command Line expects to trim off argv[0]. + SmallVector ArgsWithArgv0 { Argv0 }; + ArgsWithArgv0.append(Args.begin(), Args.end()); + + llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(), + llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n"); + + CompilerInvocation Invocation; + + Invocation.setMainExecutablePath( + llvm::sys::fs::getMainExecutable(Argv0, MainAddr)); + Invocation.setModuleName("swift_symbolgraph_extract"); + Invocation.setSDKPath(options::SDK); + Invocation.setTargetTriple(options::Target); + + std::vector FrameworkSearchPaths; + for (const auto &Path : options::FrameworkSearchPaths) { + FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false}); + } + Invocation.setFrameworkSearchPaths(FrameworkSearchPaths); + Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths; + Invocation.setImportSearchPaths(options::ImportSearchPaths); + + Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin(); + Invocation.getLangOptions().DebuggerSupport = true; + + Invocation.getFrontendOptions().EnableLibraryEvolution = true; + + Invocation.setClangModuleCachePath(options::ModuleCachePath); + Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; + + if (!options::SwiftVersion.empty()) { + using version::Version; + bool isValid = false; + if (auto Version = Version::parseVersionString(options::SwiftVersion, + SourceLoc(), nullptr)) { + if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) { + Invocation.getLangOptions().EffectiveLanguageVersion = *Effective; + isValid = true; + } + } + if (!isValid) { + llvm::errs() << "Unsupported Swift Version.\n"; + return EXIT_FAILURE; + } + } + + symbolgraphgen::SymbolGraphOptions Options { + options::OutputPath, + llvm::Triple(options::Target), + options::PrettyPrint, + AccessLevel::Public, + }; + + if (!options::MinimumAccessLevel.empty()) { + Options.MinimumAccessLevel = + llvm::StringSwitch(options::MinimumAccessLevel) + .Case("open", AccessLevel::Open) + .Case("public", AccessLevel::Public) + .Case("internal", AccessLevel::Internal) + .Case("fileprivate", AccessLevel::FilePrivate) + .Case("private", AccessLevel::Private) + .Default(AccessLevel::Public); + } + + PrintingDiagnosticConsumer DiagPrinter; + + CompilerInstance CI; + CI.getDiags().addConsumer(DiagPrinter); + + if (CI.setup(Invocation)) { + llvm::outs() << "Failed to setup compiler instance\n"; + return EXIT_FAILURE; + } + + auto M = CI.getASTContext().getModuleByName(options::ModuleName); + if (!M) { + llvm::errs() + << "Couldn't load module '" << options::ModuleName << '\'' + << " in the current SDK and search paths.\n"; + SmallVector VisibleModuleNames; + CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames); + + if (VisibleModuleNames.empty()) { + llvm::errs() << "Could not find any modules.\n"; + } else { + std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(), + [](const Identifier &A, const Identifier &B) -> bool { + return A.str() < B.str(); + }); + llvm::errs() << "Current visible modules:\n"; + for (const auto &ModuleName : VisibleModuleNames) { + llvm::errs() << ModuleName.str() << "\n"; + } + } + return EXIT_FAILURE; + } + + return symbolgraphgen::emitSymbolGraphForModule(M, + Options); +} diff --git a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp index def4356f2713d..ce0b3261257d0 100644 --- a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp +++ b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp @@ -325,14 +325,14 @@ int main(int argc, char **argv) { llvm::outs() << "Importing " << path << "... "; #ifdef SWIFT_SUPPORTS_SUBMODULES - std::vector > AccessPath; + std::vector> AccessPath; for (auto i = llvm::sys::path::begin(path); i != llvm::sys::path::end(path); ++i) if (!llvm::sys::path::is_separator((*i)[0])) AccessPath.push_back({ CI.getASTContext().getIdentifier(*i), swift::SourceLoc() }); #else - std::vector > AccessPath; + std::vector> AccessPath; AccessPath.push_back({ CI.getASTContext().getIdentifier(path), swift::SourceLoc() }); #endif diff --git a/tools/sil-func-extractor/SILFunctionExtractor.cpp b/tools/sil-func-extractor/SILFunctionExtractor.cpp index 7c04d529c33fa..3e95fb3215418 100644 --- a/tools/sil-func-extractor/SILFunctionExtractor.cpp +++ b/tools/sil-func-extractor/SILFunctionExtractor.cpp @@ -249,6 +249,8 @@ int main(int argc, char **argv) { Invocation.getLangOptions().DisableAvailabilityChecking = true; Invocation.getLangOptions().EnableAccessControl = false; Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; + if (Invocation.getLangOptions().Target.isOSBinFormatWasm()) + Invocation.getLangOptions().EnableObjCInterop = false; serialization::ExtendedValidationInfo extendedInfo; llvm::ErrorOr> FileBufOrErr = diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp index 8bba7268ff2f7..fc6fe99b0b5f7 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp @@ -37,7 +37,7 @@ struct swift::ide::api::SDKNodeInitInfo { #define KEY_STRING_ARR(X, Y) std::vector X; #include "swift/IDE/DigesterEnums.def" - ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; + swift::ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; std::vector DeclAttrs; std::vector TypeAttrs; @@ -126,7 +126,9 @@ SDKNodeTypeAlias::SDKNodeTypeAlias(SDKNodeInitInfo Info): SDKNodeDeclType::SDKNodeDeclType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclType), SuperclassUsr(Info.SuperclassUsr), SuperclassNames(Info.SuperclassNames), - EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal) {} + EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal), + HasMissingDesignatedInitializers(Info.HasMissingDesignatedInitializers), + InheritsConvenienceInitializers(Info.InheritsConvenienceInitializers) {} SDKNodeConformance::SDKNodeConformance(SDKNodeInitInfo Info): SDKNode(Info, SDKNodeKind::Conformance), @@ -1403,6 +1405,8 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD) SuperclassNames.push_back(getPrintedName(Ctx, T->getCanonicalType())); } } + HasMissingDesignatedInitializers = CD->hasMissingDesignatedInitializers(); + InheritsConvenienceInitializers = CD->inheritsSuperclassInitializers(); } if (auto *FD = dyn_cast(VD)) { @@ -1975,6 +1979,10 @@ void SDKNodeDeclType::jsonize(json::Output &out) { output(out, KeyKind::KK_superclassUsr, SuperclassUsr); output(out, KeyKind::KK_enumRawTypeName, EnumRawTypeName); output(out, KeyKind::KK_isExternal, IsExternal); + output(out, KeyKind::KK_hasMissingDesignatedInitializers, + HasMissingDesignatedInitializers); + output(out, KeyKind::KK_inheritsConvenienceInitializers, + InheritsConvenienceInitializers); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_superclassNames).data(), SuperclassNames); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_conformances).data(), Conformances); } diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.h b/tools/swift-api-digester/ModuleAnalyzerNodes.h index 8e5feb76f1016..db19837d46ebc 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.h +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.h @@ -524,6 +524,8 @@ class SDKNodeDeclType: public SDKNodeDecl { // Check whether the type declaration is pulled from an external module so we // can incorporate extensions in the interested module. bool IsExternal; + bool HasMissingDesignatedInitializers; + bool InheritsConvenienceInitializers; public: SDKNodeDeclType(SDKNodeInitInfo Info); static bool classof(const SDKNode *N); @@ -548,6 +550,13 @@ class SDKNodeDeclType: public SDKNodeDecl { return EnumRawTypeName; } + bool hasMissingDesignatedInitializers() const { + return HasMissingDesignatedInitializers; + }; + bool inheritsConvenienceInitializers() const { + return InheritsConvenienceInitializers; + }; + Optional getSuperclass() const; /// Finding the node through all children, including the inheritted ones, diff --git a/tools/swift-api-digester/ModuleDiagsConsumer.cpp b/tools/swift-api-digester/ModuleDiagsConsumer.cpp index dcb9902633de3..2964709fc4ae4 100644 --- a/tools/swift-api-digester/ModuleDiagsConsumer.cpp +++ b/tools/swift-api-digester/ModuleDiagsConsumer.cpp @@ -73,6 +73,8 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::super_class_changed: case LocalDiagID::no_longer_open: case LocalDiagID::desig_init_added: + case LocalDiagID::added_invisible_designated_init: + case LocalDiagID::not_inheriting_convenience_inits: return "/* Class Inheritance Change */"; default: return StringRef(); diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index dcc6e06131126..6253f69a9e8df 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -789,6 +789,22 @@ void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); } } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); break; } default: diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 71f7349105577..995eaaaab5eef 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -33,6 +33,7 @@ #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/ConformingMethodList.h" @@ -45,6 +46,7 @@ #include "swift/IDE/IDERequests.h" #include "swift/Index/Index.h" #include "swift/Sema/IDETypeChecking.h" +#include "swift/SyntaxParse/SyntaxTreeCreator.h" #include "swift/Markup/Markup.h" #include "swift/Config.h" #include "clang/Rewrite/Core/RewriteBuffer.h" @@ -84,6 +86,7 @@ enum class ActionType { PrintASTNotTypeChecked, PrintASTTypeChecked, PrintModule, + PrintModuleMetadata, PrintHeader, PrintSwiftFileInterface, PrintDecl, @@ -179,6 +182,8 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), "print-ast-typechecked", "Print the typechecked AST"), clEnumValN(ActionType::PrintModule, "print-module", "Print visible declarations in a module"), + clEnumValN(ActionType::PrintModuleMetadata, + "print-module-metadata", "Print meta-data in a module"), clEnumValN(ActionType::PrintHeader, "print-header", "Print visible declarations in a header file"), clEnumValN(ActionType::PrintSwiftFileInterface, @@ -716,23 +721,35 @@ removeCodeCompletionTokens(llvm::MemoryBuffer *Input, Input->getBufferIdentifier())); } -static int doTypeContextInfo(const CompilerInvocation &InitInvok, - StringRef SourceFilename, - StringRef SecondSourceFileName, - StringRef CodeCompletionToken, - bool CodeCompletionDiagnostics) { +/// Returns true on error +static bool setBufferForFile(StringRef SourceFilename, + std::unique_ptr &Buffer) { llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(SourceFilename); if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; + llvm::errs() << "error opening input file '" << SourceFilename << "':\n" + << " " << FileBufOrErr.getError().message() << '\n'; + return true; } + Buffer = std::move(FileBufOrErr.get()); + return false; +} + +static bool doCodeCompletionImpl( + CodeCompletionCallbacksFactory *callbacksFactory, + const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; unsigned Offset; std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); + FileBuf.get(), CodeCompletionToken, &Offset)); if (Offset == ~0U) { llvm::errs() << "could not find code completion token \"" @@ -746,16 +763,30 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; + if (!SecondSourceFileName.empty()) { + Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( + SecondSourceFileName); + } - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); + std::string Error; + PrintingDiagnosticConsumer PrintDiags; + CompletionInstance CompletionInst; + auto isSuccess = CompletionInst.performOperation( + Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(), + Offset, /*EnableASTCaching=*/false, Error, + CodeCompletionDiagnostics ? &PrintDiags : nullptr, + [&](CompilerInstance &CI) { + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); + return isSuccess ? 0 : 1; +} +static int doTypeContextInfo(const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { // Create a CodeCompletionConsumer. std::unique_ptr Consumer( new ide::PrintingTypeContextInfoConsumer(llvm::outs())); @@ -765,23 +796,9 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeTypeContextInfoCallbacksFactory(*Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int @@ -790,41 +807,6 @@ doConformingMethodList(const CompilerInvocation &InitInvok, StringRef CodeCompletionToken, bool CodeCompletionDiagnostics, const std::vector expectedTypeNames) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned Offset; - - std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); - - if (Offset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; - - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); - SmallVector typeNames; for (auto &name : expectedTypeNames) typeNames.push_back(name.c_str()); @@ -838,23 +820,9 @@ doConformingMethodList(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeConformingMethodListCallbacksFactory(typeNames, *Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doCodeCompletion(const CompilerInvocation &InitInvok, @@ -864,42 +832,6 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, bool CodeCompletionDiagnostics, bool CodeCompletionKeywords, bool CodeCompletionComments) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned CodeCompletionOffset; - - std::unique_ptr CleanFile( - removeCodeCompletionTokens(FileBufOrErr.get().get(), CodeCompletionToken, - &CodeCompletionOffset)); - - if (CodeCompletionOffset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset); - - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - - // Disable to build syntax tree because code-completion skips some portion of - // source text. That breaks an invariant of syntax tree building. - Invocation.getLangOptions().BuildSyntaxTree = false; - std::unique_ptr OnDiskCache; if (!options::CompletionCachePath.empty()) { OnDiskCache = llvm::make_unique( @@ -915,40 +847,21 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, // Create a factory for code completion callbacks that will feed the // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - *Consumer)); - - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, *Consumer)); - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseAndResolveImportsOnly(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doREPLCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } - StringRef BufferText = FileBufOrErr.get()->getBuffer(); + StringRef BufferText = FileBuf->getBuffer(); // Drop a single newline character from the buffer. if (BufferText.endswith("\n")) BufferText = BufferText.drop_back(1); @@ -1154,38 +1067,74 @@ static int doSyntaxColoring(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); Invocation.getLangOptions().DisableAvailabilityChecking = false; - - CompilerInstance CI; - - // Display diagnostics to stderr. - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); Invocation.getLangOptions().Playground = Playground; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getLangOptions().BuildSyntaxTree = true; - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - if (!RunTypeChecker) - CI.performParseOnly(); - else + + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + + if (RunTypeChecker) { + CompilerInstance CI; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; CI.performSema(); - unsigned BufID = CI.getInputBufferIDs().back(); - SourceFile *SF = nullptr; - for (auto Unit : CI.getMainModule()->getFiles()) { - SF = dyn_cast(Unit); - if (SF) - break; - } - assert(SF && "no source file?"); + unsigned BufID = CI.getInputBufferIDs().back(); + SourceFile *SF = nullptr; + for (auto Unit : CI.getMainModule()->getFiles()) { + SF = dyn_cast(Unit); + if (SF) + break; + } + assert(SF && "no source file?"); + + ide::SyntaxModelContext ColorContext(*SF); + PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } else { + // SourceKit doesn't set up a compiler instance at all for its syntactic + // requests, just the parser. We try to mimic that setup here to help catch + // any cases where the walker might inadvertently rely on the name lookup or + // other semantic functionality via the request evaluator. + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); + + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; + Parser.getDiagnosticEngine().addConsumer(PrintDiags); - ide::SyntaxModelContext ColorContext(*SF); - PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), - TerminalOutput); - ColorContext.walk(ColorWalker); - ColorWalker.finished(); + (void)Parser.parse(); + ide::SyntaxModelContext ColorContext(Parser.getSourceFile()); + PrintSyntaxColorWalker ColorWalker(SM, BufferID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } return 0; } @@ -1377,25 +1326,51 @@ class StructureAnnotator : public ide::SyntaxModelWalker { static int doStructureAnnotation(const CompilerInvocation &InitInvok, StringRef SourceFilename) { + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + CompilerInvocation Invocation(InitInvok); Invocation.getLangOptions().BuildSyntaxTree = true; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); - CompilerInstance CI; + // Structure annotation is run as a purely syntactic request by SourceKit. It + // doesn't set up a compiler instance at all, just the parser. We try to mimic + // that setup here to help catch any cases where the walker might inadvertently + // rely on the name lookup or other semantic functionality via the request + // evaluator. + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); + + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions( + Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseOnly(); + Parser.getDiagnosticEngine().addConsumer(PrintDiags); - unsigned BufID = CI.getInputBufferIDs().back(); - ide::SyntaxModelContext StructureContext( - CI.getMainModule()->getMainSourceFile(SourceFileKind::Main)); - StructureAnnotator Annotator(CI.getSourceMgr(), BufID); + (void)Parser.parse(); + + ide::SyntaxModelContext StructureContext(Parser.getSourceFile()); + StructureAnnotator Annotator(SM, BufferID); StructureContext.walk(Annotator); Annotator.printResult(llvm::outs()); return 0; @@ -1658,17 +1633,13 @@ static int doSemanticAnnotation(const CompilerInvocation &InitInvok, } static int doInputCompletenessTest(StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } llvm::raw_ostream &OS = llvm::outs(); OS << SourceFilename << ": "; - if (isSourceInputComplete(std::move(FileBufOrErr.get()), + if (isSourceInputComplete(std::move(FileBuf), SourceFileKind::REPL).IsComplete) { OS << "IS_COMPLETE\n"; } else { @@ -1682,7 +1653,7 @@ static int doInputCompletenessTest(StringRef SourceFilename) { //===----------------------------------------------------------------------===// static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1697,8 +1668,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName } static ModuleDecl *getModuleByFullName(ASTContext &Context, Identifier ModuleName) { - ModuleDecl *Result = Context.getModule(std::make_pair(ModuleName, - SourceLoc())); + ModuleDecl *Result = Context.getModule({ Located(ModuleName,SourceLoc()) }); if (!Result || Result->failedToLoad()) return nullptr; return Result; @@ -2070,6 +2040,76 @@ static int doPrintModuleGroups(const CompilerInvocation &InitInvok, return ExitCode; } +static void printModuleMetadata(ModuleDecl *MD) { + auto &OS = llvm::outs(); + MD->collectLinkLibraries([&](LinkLibrary lib) { + OS << "link library: " << lib.getName() + << ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n"; + }); +} + +static int doPrintModuleMetaData(const CompilerInvocation &InitInvok, + const std::vector ModulesToPrint) { + CompilerInvocation Invocation(InitInvok); + + CompilerInstance CI; + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; + registerIDERequestFunctions(CI.getASTContext().evaluator); + auto &Context = CI.getASTContext(); + + // Load standard library so that Clang importer can use it. + auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName); + if (!Stdlib) { + llvm::errs() << "Failed loading stdlib\n"; + return 1; + } + int ExitCode = 0; + for (StringRef ModuleToPrint : ModulesToPrint) { + if (ModuleToPrint.empty()) { + ExitCode = 1; + continue; + } + + // Get the (sub)module to print. + auto *M = getModuleByFullName(Context, ModuleToPrint); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleToPrint + << "'\n"; + ExitCode = 1; + continue; + } + + // Split the module path. + std::vector ModuleName; + while (!ModuleToPrint.empty()) { + StringRef SubModuleName; + std::tie(SubModuleName, ModuleToPrint) = ModuleToPrint.split('.'); + ModuleName.push_back(SubModuleName); + } + assert(!ModuleName.empty()); + + // FIXME: If ModuleToPrint is a submodule, get its top-level module, which + // will be the DeclContext for all of its Decls since we don't have first- + // class submodules. + if (ModuleName.size() > 1) { + M = getModuleByFullName(Context, ModuleName[0]); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleName[0] + << "'\n"; + ExitCode = 1; + continue; + } + } + printModuleMetadata(M); + } + + return ExitCode; +} + static int doPrintModules(const CompilerInvocation &InitInvok, const std::vector ModulesToPrint, const std::vector GroupsToPrint, @@ -2721,7 +2761,7 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok, for (auto &import : scratch) { llvm::outs() << "\t" << import.second->getName(); for (auto accessPathPiece : import.first) { - llvm::outs() << "." << accessPathPiece.first; + llvm::outs() << "." << accessPathPiece.Item; } if (import.second->isClangModule()) @@ -3200,16 +3240,12 @@ static int doTestCreateCompilerInvocation(ArrayRef Args) { } static int doTestCompilerInvocationFromModule(StringRef ModuleFilePath) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(ModuleFilePath); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return -1; - } + std::unique_ptr FileBuf; + if (setBufferForFile(ModuleFilePath, FileBuf)) + return 1; CompilerInvocation CI; - StringRef Data = FileBufOrErr.get()->getBuffer(); + StringRef Data = FileBuf->getBuffer(); static_assert(static_cast(serialization::Status::Valid) == 0, "Status::Valid should be a successful exit"); return static_cast(CI.loadFromSerializedAST(Data)); @@ -3580,7 +3616,10 @@ int main(int argc, char *argv[]) { } break; } - + case ActionType::PrintModuleMetadata: { + ExitCode = doPrintModuleMetaData(InitInvok, options::ModuleToPrint); + break; + } case ActionType::PrintHeader: { ExitCode = doPrintHeaders( InitInvok, options::HeaderToPrint, PrintOpts, diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 83417f6cf97fd..22c885b97c2cb 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -71,7 +71,9 @@ Action(llvm::cl::desc("kind:"), llvm::cl::init(RefactoringKind::None), "trailingclosure", "Perform trailing closure refactoring"), clEnumValN(RefactoringKind::ReplaceBodiesWithFatalError, "replace-bodies-with-fatalError", "Perform trailing closure refactoring"), - clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"))); + clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"), + clEnumValN(RefactoringKind::ConvertToComputedProperty, + "convert-to-computed-property", "Convert from field initialization to computed property"))); static llvm::cl::opt diff --git a/tools/swift-remoteast-test/CMakeLists.txt b/tools/swift-remoteast-test/CMakeLists.txt index 54987f792c12f..ecf4c953a1857 100644 --- a/tools/swift-remoteast-test/CMakeLists.txt +++ b/tools/swift-remoteast-test/CMakeLists.txt @@ -7,9 +7,6 @@ target_link_libraries(swift-remoteast-test swiftFrontendTool swiftRemoteAST) set_target_properties(swift-remoteast-test PROPERTIES ENABLE_EXPORTS 1) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift-remoteast-test PRIVATE edit) -endif() # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) diff --git a/tools/swift-remoteast-test/swift-remoteast-test.cpp b/tools/swift-remoteast-test/swift-remoteast-test.cpp index 391c7b3edb98e..9b172eb8ab394 100644 --- a/tools/swift-remoteast-test/swift-remoteast-test.cpp +++ b/tools/swift-remoteast-test/swift-remoteast-test.cpp @@ -72,7 +72,7 @@ static RemoteASTContext &getRemoteASTContext() { // FIXME: swiftcall /// func printType(forMetadata: Any.Type) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printMetadataType(const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -90,7 +90,7 @@ printMetadataType(const Metadata *typeMetadata) { // FIXME: swiftcall /// func printDynamicType(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printHeapMetadataType(void *object) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -145,7 +145,7 @@ static void printMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMemberOffset(forType: Any.Type, memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ false); @@ -154,7 +154,7 @@ printTypeMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMetadataMemberOffset(forType: Any.Type, /// memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMetadataMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ true); @@ -162,7 +162,7 @@ printTypeMetadataMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printDynamicTypeAndAddressForExistential(_: T) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printDynamicTypeAndAddressForExistential(void *object, const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); @@ -192,7 +192,7 @@ printDynamicTypeAndAddressForExistential(void *object, // FIXME: swiftcall /// func stopRemoteAST(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED stopRemoteAST() { if (remoteContext) remoteContext.reset(); diff --git a/unittests/AST/ArithmeticEvaluator.cpp b/unittests/AST/ArithmeticEvaluator.cpp index 7738ba8e55fea..ed85576e07c57 100644 --- a/unittests/AST/ArithmeticEvaluator.cpp +++ b/unittests/AST/ArithmeticEvaluator.cpp @@ -210,7 +210,9 @@ TEST(ArithmeticEvaluator, Simple) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/true); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); @@ -333,7 +335,9 @@ TEST(ArithmeticEvaluator, Cycle) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/false); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); diff --git a/unittests/AST/DiagnosticConsumerTests.cpp b/unittests/AST/DiagnosticConsumerTests.cpp index d347f8f32845f..77032b705a935 100644 --- a/unittests/AST/DiagnosticConsumerTests.cpp +++ b/unittests/AST/DiagnosticConsumerTests.cpp @@ -11,13 +11,14 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticConsumer.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceManager.h" #include "gtest/gtest.h" using namespace swift; namespace { - using ExpectedDiagnostic = std::pair; + using ExpectedDiagnostic = Located; class ExpectationDiagnosticConsumer: public DiagnosticConsumer { ExpectationDiagnosticConsumer *previous; @@ -37,7 +38,7 @@ namespace { void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override { ASSERT_FALSE(expected.empty()); - EXPECT_EQ(std::make_pair(Info.Loc, Info.FormatString), expected.front()); + EXPECT_EQ(Located(Info.FormatString, Info.Loc), expected.front()); expected.erase(expected.begin()); } @@ -83,7 +84,7 @@ TEST(FileSpecificDiagnosticConsumer, InvalidLocDiagsGoToEveryConsumer) { (void)sourceMgr.addMemBufferCopy("abcde", "A"); (void)sourceMgr.addMemBufferCopy("vwxyz", "B"); - ExpectedDiagnostic expected[] = { {SourceLoc(), "dummy"} }; + ExpectedDiagnostic expected[] = { Located("dummy", SourceLoc()) }; auto consumerA = llvm::make_unique( nullptr, expected); auto consumerUnaffiliated = llvm::make_unique( @@ -116,14 +117,14 @@ TEST(FileSpecificDiagnosticConsumer, ErrorsWithLocationsGoToExpectedConsumers) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {middleOfA, "middle"}, - {backOfA, "back"}, + {"front", frontOfA}, + {"middle", middleOfA}, + {"back", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -170,17 +171,17 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {frontOfB, "front"}, - {middleOfA, "middle"}, - {middleOfB, "middle"}, - {backOfA, "back"}, - {backOfB, "back"} + {"front", frontOfA}, + {"front", frontOfB}, + {"middle", middleOfA}, + {"middle", middleOfB}, + {"back", backOfA}, + {"back", backOfB} }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -221,14 +222,14 @@ TEST(FileSpecificDiagnosticConsumer, WarningsAndRemarksAreTreatedLikeErrors) { SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {frontOfB, "warning"}, - {frontOfA, "remark"}, - {frontOfB, "remark"}, + {"warning", frontOfA}, + {"warning", frontOfB}, + {"remark", frontOfA}, + {"remark", frontOfB}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {frontOfB, "remark"}, + {"warning", frontOfB}, + {"remark", frontOfB}, }; auto consumerA = llvm::make_unique( @@ -272,20 +273,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrors) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -335,20 +336,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToWarningsAndRemarks) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "remark"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"warning", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"remark", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -401,17 +402,17 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrorsEvenAcrossFiles) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -462,20 +463,20 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -522,16 +523,16 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {SourceLoc(), "note"}, - {frontOfB, "error"}, - {SourceLoc(), "note"}, - {frontOfA, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfA}, + {"note", SourceLoc()}, + {"error", frontOfB}, + {"note", SourceLoc()}, + {"error", frontOfA}, + {"note", SourceLoc()}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfB}, + {"note", SourceLoc()}, }; auto consumerA = llvm::make_unique( diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index d246d8a0fce33..70d5e2edebb6b 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -30,7 +30,7 @@ static void declareOptionalType(ASTContext &ctx, SourceFile *fileForLookups, auto decl = new (ctx) EnumDecl(SourceLoc(), name, SourceLoc(), /*inherited*/{}, params, fileForLookups); wrapped->setDeclContext(decl); - fileForLookups->Decls.push_back(decl); + fileForLookups->addTopLevelDecl(decl); } TestContext::TestContext(ShouldDeclareOptionalTypes optionals) diff --git a/unittests/ClangImporter/CMakeLists.txt b/unittests/ClangImporter/CMakeLists.txt index 4f86c3da90e39..f6041b16f2760 100644 --- a/unittests/ClangImporter/CMakeLists.txt +++ b/unittests/ClangImporter/CMakeLists.txt @@ -7,4 +7,5 @@ target_link_libraries(SwiftClangImporterTests swiftClangImporter swiftParse swiftAST + LLVMBitstreamReader ) diff --git a/utils/build-script b/utils/build-script index b1d060328bc6d..be4d66a8d43e5 100755 --- a/utils/build-script +++ b/utils/build-script @@ -119,6 +119,20 @@ class BuildScriptInvocation(object): "--android-icu-i18n-include, and --android-icu-data " "must be specified") + if args.wasm: + if args.wasi_sdk is None or \ + args.wasi_icu_uc is None or \ + args.wasi_icu_uc_include is None or \ + args.wasi_icu_i18n is None or \ + args.wasi_icu_i18n_include is None or \ + args.wasi_icu_data is None: + diagnostics.fatal( + "when building for WebAssembly, --wasi-sdk, " + "--wasi-icu-uc, " + "--wasi-icu-uc-include, --wasi-icu-i18n, " + "--wasi-icu-i18n-include, and --wasi-icu-data " + "must be specified") + targets_needing_toolchain = [ 'build_indexstoredb', 'build_sourcekitlsp', @@ -213,6 +227,9 @@ class BuildScriptInvocation(object): elif args.android_arch == "aarch64": args.stdlib_deployment_targets.append( StdlibDeploymentTarget.Android.aarch64.name) + if args.wasm: + args.stdlib_deployment_targets.append( + StdlibDeploymentTarget.WASI.wasm32.name) # Infer platform flags from manually-specified configure targets. # This doesn't apply to Darwin platforms, as they are @@ -498,6 +515,8 @@ class BuildScriptInvocation(object): impl_args += ["--skip-build-watchos-simulator"] if not args.build_android: impl_args += ["--skip-build-android"] + if not args.build_wasm: + impl_args += ["--skip-build-wasm"] if not args.build_clang_tools_extra: impl_args += ["--skip-build-clang-tools-extra"] @@ -538,6 +557,10 @@ class BuildScriptInvocation(object): impl_args += ["--skip-test-android"] if not args.test_android_host: impl_args += ["--skip-test-android-host"] + if not args.test_wasm: + impl_args += ["--skip-test-wasm"] + if not args.test_wasm_host: + impl_args += ["--skip-test-wasm-host"] if args.build_runtime_with_host_compiler: impl_args += ["--build-runtime-with-host-compiler"] if args.validation_test: @@ -578,6 +601,16 @@ class BuildScriptInvocation(object): args.android_deploy_device_path, ] + if args.wasm: + impl_args += [ + "--wasi-sdk", args.wasi_sdk, + "--wasi-icu-uc", args.wasi_icu_uc, + "--wasi-icu-uc-include", args.wasi_icu_uc_include, + "--wasi-icu-i18n", args.wasi_icu_i18n, + "--wasi-icu-i18n-include", args.wasi_icu_i18n_include, + "--wasi-icu-data", args.wasi_icu_data, + ] + if platform.system() == 'Darwin': impl_args += [ "--toolchain-prefix", diff --git a/utils/build-script-impl b/utils/build-script-impl index fce8102a7f453..4423086de2f6d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -115,6 +115,7 @@ KNOWN_SETTINGS=( skip-build-watchos-device "" "set to skip building Swift stdlibs for Apple watchOS devices (i.e. build simulators only)" skip-build-watchos-simulator "" "set to skip building Swift stdlibs for Apple watchOS simulators (i.e. build devices only)" skip-build-android "" "set to skip building Swift stdlibs for Android" + skip-build-wasm "" "set to skip building Swift stdlibs for WebAssembly" skip-build-lldb "" "set to skip building LLDB" skip-build-llbuild "" "set to skip building llbuild" skip-build-libcxx "" "set to skip building libcxx" @@ -150,6 +151,8 @@ KNOWN_SETTINGS=( skip-test-watchos-host "" "set to skip testing the host parts of the watchOS toolchain" skip-test-android "" "set to skip testing Swift stdlibs for Android" skip-test-android-host "" "set to skip testing the host parts of the Android toolchain" + skip-test-wasm "" "set to skip testing Swift stdlibs for WebAssembly" + skip-test-wasm-host "" "set to skip testing the host parts of the WebAssembly toolchain" validation-test "0" "set to run the validation test suite" long-test "0" "set to run the long test suite" stress-test "0" "set to run the stress test suite" @@ -233,6 +236,12 @@ KNOWN_SETTINGS=( android-icu-data "" "Path to libicudata.so" android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" android-arch "armv7" "The Android target architecture when building for Android" + wasi-sdk "" "An absolute path to the WASI SDK that will be used as a libc implementation for Wasm builds" + wasi-icu-uc "" "Path to libicuuc.so" + wasi-icu-uc-include "" "Path to a directory containing headers for libicuuc" + wasi-icu-i18n "" "Path to libicui18n.so" + wasi-icu-i18n-include "" "Path to a directory containing headers libicui18n" + wasi-icu-data "" "Path to libicudata.so" check-args-only "" "set to check all arguments are known. Exit with status 0 if success, non zero otherwise" common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" cmark-cmake-options "" "CMake options used for all cmark targets" @@ -247,8 +256,6 @@ KNOWN_SETTINGS=( swift-cmake-options "" "CMake options used for all swift targets" xctest-cmake-options "" "CMake options used for all xctest targets" playgroundsupport-cmake-options "" "CMake options used for all playgroundsupport targets" - # TODO: Remove this some time later. - user-config-args "" "**Renamed to --extra-cmake-options**: User-supplied arguments to cmake when used to do configuration." only-execute "all" "Only execute the named action (see implementation)" llvm-lit-args "" "If set, override the lit args passed to LLVM" clang-profile-instr-use "" "If set, profile file to use for clang PGO while building llvm/clang" @@ -326,14 +333,6 @@ function to_varname() { toupper "$(echo $1 | tr '-' '_')" } -function set_lldb_build_mode() { - if [[ "${LLDB_BUILD_TYPE}" == "RelWithDebInfo" ]]; then - LLDB_BUILD_MODE="CustomSwift-Release" - else - LLDB_BUILD_MODE="CustomSwift-${LLDB_BUILD_TYPE}" - fi -} - function is_llvm_lto_enabled() { if [[ "${LLVM_ENABLE_LTO}" == "thin" ]] || [[ "${LLVM_ENABLE_LTO}" == "full" ]]; then @@ -422,7 +421,8 @@ function verify_host_is_supported() { | watchsimulator-i386 \ | watchos-armv7k \ | android-armv7 \ - | android-aarch64) + | android-aarch64 \ + | wasi-wasm32) ;; *) echo "Unknown host tools target: ${host}" @@ -465,75 +465,99 @@ function set_build_options_for_host() { SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" llvm_target_arch="ARM" ;; + linux-*) + SWIFT_HOST_VARIANT="linux" + SWIFT_HOST_VARIANT_SDK="LINUX" + case ${host} in + linux-x86_64) + SWIFT_HOST_VARIANT_ARCH="x86_64" + ;; + linux-i686) + SWIFT_HOST_VARIANT_ARCH="i686" + ;; + linux-armv6) + SWIFT_HOST_VARIANT_ARCH="armv6" + SWIFT_HOST_TRIPLE="armv6-unknown-linux-gnueabihf" + llvm_target_arch="ARM" + ;; + linux-armv7) + SWIFT_HOST_VARIANT_ARCH="armv7" + SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" + llvm_target_arch="ARM" + ;; + linux-aarch64) + SWIFT_HOST_VARIANT_ARCH="aarch64" + ;; + linux-powerpc64) + SWIFT_HOST_VARIANT_ARCH="powerpc64" + ;; + linux-powerpc64le) + SWIFT_HOST_VARIANT_ARCH="powerpc64le" + ;; + linux-s390x) + SWIFT_HOST_VARIANT_ARCH="s390x" + ;; + esac + ;; macosx-* | iphoneos-* | iphonesimulator-* | \ appletvos-* | appletvsimulator-* | \ watchos-* | watchsimulator-*) case ${host} in macosx-x86_64) - xcrun_sdk_name="macosx" llvm_target_arch="" SWIFT_HOST_TRIPLE="x86_64-apple-macosx${DARWIN_DEPLOYMENT_VERSION_OSX}" SWIFT_HOST_VARIANT_SDK="OSX" cmake_osx_deployment_target="${DARWIN_DEPLOYMENT_VERSION_OSX}" ;; iphonesimulator-i386) - xcrun_sdk_name="iphonesimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" ;; iphonesimulator-x86_64) - xcrun_sdk_name="iphonesimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" ;; iphoneos-armv7) - xcrun_sdk_name="iphoneos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; iphoneos-armv7s) - xcrun_sdk_name="iphoneos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7s-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; iphoneos-arm64) - xcrun_sdk_name="iphoneos" llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" ;; appletvsimulator-x86_64) - xcrun_sdk_name="appletvsimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" SWIFT_HOST_VARIANT_SDK="TVOS_SIMULATOR" cmake_osx_deployment_target="" ;; appletvos-arm64) - xcrun_sdk_name="appletvos" llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" SWIFT_HOST_VARIANT_SDK="TVOS" cmake_osx_deployment_target="" ;; watchsimulator-i386) - xcrun_sdk_name="watchsimulator" llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" SWIFT_HOST_VARIANT_SDK="WATCHOS_SIMULATOR" cmake_osx_deployment_target="" ;; watchos-armv7k) - xcrun_sdk_name="watchos" llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7k-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" SWIFT_HOST_VARIANT_SDK="WATCHOS" @@ -542,10 +566,14 @@ function set_build_options_for_host() { esac if [[ "${DARWIN_SDK_DEPLOYMENT_TARGETS}" != "" ]]; then - local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS); unset IFS for target in "${DARWIN_SDK_DEPLOYMENT_TARGETS[@]}"; do - local IFS="-"; triple=($target) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS="-"; triple=($target); unset IFS sdk_target=$(toupper ${triple[0]}_${triple[1]}) swift_cmake_options+=( "-DSWIFTLIB_DEPLOYMENT_VERSION_${sdk_target}=${triple[2]}" @@ -553,15 +581,17 @@ function set_build_options_for_host() { done fi + cmake_os_sysroot="$(xcrun --sdk ${platform} --show-sdk-path)" + cmark_cmake_options=( -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${cmake_osx_deployment_target}" ) llvm_cmake_options=( -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCOMPILER_RT_ENABLE_IOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_WATCHOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_TVOS:BOOL=FALSE @@ -569,26 +599,12 @@ function set_build_options_for_host() { -DLLVM_ENABLE_MODULES:BOOL="$(true_false ${LLVM_ENABLE_MODULES})" ) if [[ $(is_llvm_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) == "TRUE" ]]; then - llvm_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( "-DLLVM_PARALLEL_LINK_JOBS=${LLVM_NUM_PARALLEL_LTO_LINK_JOBS}" ) fi if [[ $(is_swift_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) = "TRUE" ]]; then - swift_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( -DLLVM_ENABLE_MODULE_DEBUGGING:BOOL=NO ) @@ -811,12 +827,6 @@ done # TODO: Rename this argument LOCAL_HOST=$HOST_TARGET -# TODO: Remove this some time later. -if [[ "${USER_CONFIG_ARGS}" ]]; then - echo "Error: --user-config-args is renamed to --extra-cmake-options." 1>&2 - exit 1 -fi - if [[ "${CHECK_ARGS_ONLY}" ]]; then exit 0 fi @@ -906,26 +916,6 @@ function false_true() { fi } -function cmake_version() { - "${CMAKE}" --version | grep "cmake version" | cut -f 3 -d " " -} - -function cmake_needs_to_specify_standard_computed_defaults() { - if [[ $(cmake_version) = "3.4.0" ]]; then - echo "TRUE" - else - echo "FALSE" - fi -} - -function make_relative_symlink() { - local SOURCE=$1 - local TARGET=$2 - local TARGET_DIR=$(dirname "$2") - local RELATIVE_SOURCE=$(python -c "import os.path; print(os.path.relpath(\"${SOURCE}\", \"${TARGET_DIR}\"))") - call ln -sf "${RELATIVE_SOURCE}" "${TARGET}" -} - # Sanitize the list of cross-compilation targets. # # In the Build/Host/Target paradigm: @@ -1061,28 +1051,6 @@ function get_stdlib_targets_for_host() { fi } -function should_build_stdlib_target() { - local stdlib_target=$1 - local host=$2 - if [[ "${BUILD_STDLIB_DEPLOYMENT_TARGETS}" == "all" ]]; then - echo 1 - else - # Only build the stdlib targets in 'build-stdlib-deployment-targets' - local build_list=($BUILD_STDLIB_DEPLOYMENT_TARGETS) - for t in "${build_list[@]}"; do - if [[ "${t}" == "${stdlib_target}" ]]; then - echo 1 - fi - done - # As with 'stdlib-deployment-targets', 'build-stdlib-deployment-targets' - # only applies to the LOCAL_HOST. For cross-tools hosts, always allow - # their one-and-only stdlib-target to build. - if [[ $(is_cross_tools_host ${host}) ]] && [[ "${stdlib_target}" == "${host}" ]]; then - echo 1 - fi - fi -} - # # Calculate source directories for each product. # @@ -1294,6 +1262,9 @@ function common_cross_c_flags() { android-arm64) echo -n " -arch aarch64" ;; + wasi-wasm32) + echo -n " -arch wasm32" + ;; esac } @@ -1693,6 +1664,18 @@ for host in "${ALL_HOSTS[@]}"; do ) fi + if [[ ! "${SKIP_BUILD_WASM}" ]]; then + cmake_options=( + "${cmake_options[@]}" + -DSWIFT_WASI_SDK_PATH:STRING="${WASI_SDK}" + -DSWIFT_WASI_wasm32_ICU_UC:STRING="${WASI_ICU_UC}" + -DSWIFT_WASI_wasm32_ICU_UC_INCLUDE:STRING="${WASI_ICU_UC_INCLUDE}" + -DSWIFT_WASI_wasm32_ICU_I18N:STRING="${WASI_ICU_I18N}" + -DSWIFT_WASI_wasm32_ICU_I18N_INCLUDE:STRING="${WASI_ICU_I18N_INCLUDE}" + -DSWIFT_WASI_wasm32_ICU_DATA:STRING="${WASI_ICU_DATA}" + ) + fi + if [[ "${DARWIN_OVERLAY_TARGET}" != "" ]]; then # Split LOCAL_HOST into a pair ``arch-sdk`` # Example LOCAL_HOST: macosx-x86_64 @@ -2139,18 +2122,6 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - # Staging: require opt-in for building with dispatch - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi - # FIXME: Always re-build XCTest on non-darwin platforms. # The Swift project might have been changed, but CMake might # not be aware and will not rebuild. @@ -2173,7 +2144,10 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} + + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules # NOTE(compnerd) we disable tests because XCTest is not ready # yet, but we will reconfigure when the time comes. @@ -2609,16 +2583,6 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" LLVM_BIN="$(build_directory_bin ${LOCAL_HOST} llvm)" @@ -2633,7 +2597,10 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} + + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules -DENABLE_TESTING:BOOL=YES -DXCTest_DIR=$(build_directory ${host} xctest)/cmake/modules diff --git a/utils/build-toolchain b/utils/build-toolchain index 4edcc957bc40e..f89f30f3faf1d 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -98,15 +98,15 @@ set -x YEAR=$(date +"%Y") MONTH=$(date +"%m") DAY=$(date +"%d") -TOOLCHAIN_VERSION="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" +TOOLCHAIN_VERSION="5.0.${YEAR}${MONTH}${DAY}" +TOOLCHAIN_NAME="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" DARWIN_TOOLCHAIN_VERSION="0.0.${YEAR}${MONTH}${DAY}" -ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}.tar.gz" -SYM_ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}-symbols.tar.gz" +ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}.tar.gz" +SYM_ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}-symbols.tar.gz" BUNDLE_PREFIX=${BUNDLE_PREFIX:?Please specify a bundle prefix} BUNDLE_IDENTIFIER="${BUNDLE_PREFIX}.${YEAR}${MONTH}${DAY}" DISPLAY_NAME_SHORT="Local Swift Development Snapshot" DISPLAY_NAME="${DISPLAY_NAME_SHORT} ${YEAR}-${MONTH}-${DAY}" -TOOLCHAIN_NAME="${TOOLCHAIN_VERSION}" SWIFT_INSTALLABLE_PACKAGE="${RESULT_DIR}/${ARCHIVE}" SWIFT_INSTALL_DIR="${RESULT_DIR}/swift-nightly-install" @@ -126,5 +126,5 @@ DISTCC_FLAG="${DISTCC_FLAG}" darwin_toolchain_display_name="${DISPLAY_NAME}" \ darwin_toolchain_display_name_short="${DISPLAY_NAME_SHORT}" \ darwin_toolchain_xctoolchain_name="${TOOLCHAIN_NAME}" \ - darwin_toolchain_version="${DARWIN_TOOLCHAIN_VERSION}" \ + darwin_toolchain_version="${TOOLCHAIN_VERSION}" \ darwin_toolchain_alias="Local" diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index 4308fb7b2fbff..cdd4d250f3d3e 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -139,6 +139,7 @@ def _apply_default_arguments(args): args.build_tvos = False args.build_watchos = False args.build_android = False + args.build_wasm = False args.build_benchmarks = False args.build_external_benchmarks = False args.build_lldb = False @@ -168,6 +169,9 @@ def _apply_default_arguments(args): if not args.android or not args.build_android: args.build_android = False + if not args.wasm or not args.build_wasm: + args.build_wasm = False + # --test-paths implies --test and/or --validation-test # depending on what directories/files have been specified. if args.test_paths: @@ -204,6 +208,7 @@ def _apply_default_arguments(args): args.test_tvos = False args.test_watchos = False args.test_android = False + args.test_wasm = False args.test_swiftpm = False args.test_swiftsyntax = False args.test_indexstoredb = False @@ -250,11 +255,19 @@ def _apply_default_arguments(args): if not args.test_android: args.test_android_host = False + if not args.build_wasm: + args.test_wasm = False + args.test_wasm_host = False + + if not args.test_android: + args.test_android_host = False + if not args.host_test: args.test_ios_host = False args.test_tvos_host = False args.test_watchos_host = False args.test_android_host = False + args.test_wasm_host = False def create_argument_parser(): @@ -336,6 +349,9 @@ def create_argument_parser(): option('--android', toggle_true, help='also build for Android') + option('--wasm', toggle_true, + help='also build for WebAssembly') + option('--swift-analyze-code-coverage', store, choices=['false', 'not-merged', 'merged'], # so CMake can see the inert mode as a false value @@ -924,6 +940,9 @@ def create_argument_parser(): option('--skip-build-android', toggle_false('build_android'), help='skip building Swift stdlibs for Android') + option('--skip-build-wasm', toggle_false('build_wasm'), + help='skip building Swift stdlibs for WebAssembly') + option('--skip-build-benchmarks', toggle_false('build_benchmarks'), help='skip building Swift Benchmark Suite') @@ -980,6 +999,14 @@ def create_argument_parser(): help='skip testing Android device targets on the host machine (the ' 'phone itself)') + option('--skip-test-wasm', + toggle_false('test_wasm'), + help='skip testing all WebAssembly targets.') + option('--skip-test-wasm-host', + toggle_false('test_wasm_host'), + help='skip testing WebAssembly device targets on the host machine (the ' + 'WebAssembly runtime)') + option('--skip-test-swiftpm', toggle_false('test_swiftpm'), help='skip testing swiftpm') option('--skip-test-swiftsyntax', toggle_false('test_swiftsyntax'), @@ -1048,6 +1075,23 @@ def create_argument_parser(): 'Currently only armv7 and aarch64 are supported. ' '%(default)s is the default.') + in_group('Build settings for Android') + + option('--wasi-sdk', store_path, + help='An absolute path to WASI SDK that will be used as a libc ' + 'implementation for Wasm builds') + + option('--wasi-icu-uc', store_path, + help='Path to libicuuc.so') + option('--wasi-icu-uc-include', store_path, + help='Path to a directory containing headers for libicuuc') + option('--wasi-icu-i18n', store_path, + help='Path to libicui18n.so') + option('--wasi-icu-i18n-include', store_path, + help='Path to a directory containing headers libicui18n') + option('--wasi-icu-data', store_path, + help='Path to libicudata.so') + # ------------------------------------------------------------------------- in_group('Experimental language features') diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index fbfbdac2e233b..633feca51070a 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -62,7 +62,7 @@ kind='ImplementsAttributeArguments'), Child('DifferentiableArguments', kind='DifferentiableAttributeArguments'), - Child('DerivativeArguments', + Child('DerivativeRegistrationArguments', kind='DerivativeRegistrationAttributeArguments'), Child('NamedAttributeString', kind='NamedAttributeStringArgument'), @@ -261,11 +261,11 @@ Node('DifferentiationParamList', kind='SyntaxCollection', element='DifferentiationParam'), - # differentiation-param -> ('self' | identifer) ','? + # differentiation-param -> ('self' | identifer | integer-literal) ','? Node('DifferentiationParam', kind='Syntax', description=''' - A differentiation parameter: either the "self" identifier or a - function parameter name. + A differentiation parameter: either the "self" identifier, a function + parameter name, or a function parameter index. ''', traits=['WithTrailingComma'], children=[ @@ -273,6 +273,7 @@ node_choices=[ Child('Self', kind='SelfToken'), Child('Name', kind='IdentifierToken'), + Child('Index', kind='IntegerLiteralToken'), ]), Child('TrailingComma', kind='CommaToken', is_optional=True), ]), @@ -295,14 +296,16 @@ ]), # The argument of the derivative registration attribute - # '@derivative(of: ...)'. + # '@derivative(of: ...)' and the transpose registration attribute + # '@transpose(of: ...)'. + # # derivative-registration-attr-arguments -> # 'of' ':' func-decl-name ','? differentiation-params-clause? Node('DerivativeRegistrationAttributeArguments', kind='Syntax', description=''' - The arguments for the '@derivative(of:)' attribute: the 'of:' label, - the original declaration name, and an optional differentiation - parameter list. + The arguments for the '@derivative(of:)' and '@transpose(of:)' + attributes: the 'of:' label, the original declaration name, and an + optional differentiation parameter list. ''', children=[ Child('OfLabel', kind='IdentifierToken', text_choices=['of'], @@ -311,13 +314,52 @@ The colon separating the "of" label and the original declaration name. '''), - Child('Original', kind='FunctionDeclName', - description='The referenced original declaration.'), + Child('OriginalDeclName', kind='QualifiedDeclName', + description='The referenced original declaration name.'), Child('Comma', kind='CommaToken', is_optional=True), Child('DiffParams', kind='DifferentiationParamsClause', is_optional=True), ]), + # An optionally qualified declaration name. + # Currently used only for `@derivative` and `@transpose` attribute. + # TODO(TF-1066): Use module qualified name syntax/parsing instead of custom + # qualified name syntax/parsing. + # + # qualified-decl-name -> + # base-type? '.'? (identifier | operator) decl-name-arguments? + # base-type -> + # member-type-identifier | base-type-identifier + Node('QualifiedDeclName', kind='Syntax', + description=''' + An optionally qualified function declaration name (e.g. `+(_:_:)`, + `A.B.C.foo(_:_:)`). + ''', + children=[ + Child('BaseType', kind='Type', description=''' + The base type of the qualified name, optionally specified. + ''', is_optional=True), + Child('Dot', kind='Token', + token_choices=[ + 'PeriodToken', 'PrefixPeriodToken' + ], is_optional=True), + Child('Name', kind='Token', description=''' + The base name of the referenced function. + ''', + token_choices=[ + 'IdentifierToken', + 'UnspacedBinaryOperatorToken', + 'SpacedBinaryOperatorToken', + 'PrefixOperatorToken', + 'PostfixOperatorToken', + ]), + Child('Arguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the referenced function, optionally + specified. + '''), + ]), + # func-decl-name -> (identifier | operator) decl-name-arguments? # NOTE: This is duplicated with `DeclName` above. Change `DeclName` # description and use it if possible. diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 9d30f23d647c0..5bcadc30f4a35 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -243,6 +243,7 @@ 'FunctionDeclName': 239, 'PoundFilePathExpr': 240, 'DerivativeRegistrationAttributeArguments': 241, + 'QualifiedDeclName': 242, } diff --git a/utils/lldb/lldbSwiftDataFormatters.py b/utils/lldb/lldbSwiftDataFormatters.py new file mode 100644 index 0000000000000..17d5d2d0817bb --- /dev/null +++ b/utils/lldb/lldbSwiftDataFormatters.py @@ -0,0 +1,47 @@ +""" +LLDB Formatters for LLVM data types for use in the swift project. + +Load into LLDB with 'command script import /path/to/lldbDataFormatters.py' +""" + +import sys + + +def __lldb_init_module(debugger, internal_dict): + tName = 'lldbSwiftDataFormatters.SmallBitVectorSummaryProvider' + debugger.HandleCommand('type summary add -w llvm ' + '-F %s -x "^llvm::SmallBitVector$"' % tName) + + +def SmallBitVectorSummaryProvider(valobj, internal_dict): + underlyingValue = valobj.GetChildMemberWithName('X').GetValueAsUnsigned() + numBaseBits = 32 + is64Bit = sys.maxsize > 2**32 + if is64Bit: + numBaseBits = 64 + smallNumRawBits = numBaseBits - 1 + smallNumSizeBits = None + if numBaseBits == 32: + smallNumSizeBits = 5 + elif numBaseBits == 64: + smallNumSizeBits = 6 + else: + smallNumSizeBits = smallNumRawBits + smallNumDataBits = smallNumRawBits - smallNumSizeBits + + # If our underlying value is not small, print we can not dump large values. + isSmallMask = 1 + if (underlyingValue & isSmallMask) == 0: + return '' + + smallRawBits = underlyingValue >> 1 + smallSize = smallRawBits >> smallNumDataBits + bits = smallRawBits & ((1 << (smallSize + 1)) - 1) + res = "[" + for i in reversed(range(0, smallSize)): + if bool(bits & (1 << i)): + res += '1' + else: + res += '0' + res += "]" + return res diff --git a/utils/lldb/lldbToolBox.py b/utils/lldb/lldbToolBox.py index 61200493590cd..54e1999759cfd 100644 --- a/utils/lldb/lldbToolBox.py +++ b/utils/lldb/lldbToolBox.py @@ -22,6 +22,8 @@ LLVM_REPO = os.path.join(REPO_BASE, "llvm") LLVM_DATAFORMATTER_PATH = os.path.join(LLVM_REPO, "utils", "lldbDataFormatters.py") +SWIFT_DATAFORMATTER_PATH = os.path.join(SWIFT_REPO, "utils", + "lldb", "lldbSwiftDataFormatters.py") def import_llvm_dataformatters(debugger): @@ -33,6 +35,15 @@ def import_llvm_dataformatters(debugger): print("Loaded LLVM data formatters.") +def import_swift_dataformatters(debugger): + if not os.access(SWIFT_DATAFORMATTER_PATH, os.F_OK): + print("WARNING! Could not find Swift data formatters!") + return + cmd = 'command script import {}'.format(SWIFT_DATAFORMATTER_PATH) + debugger.HandleCommand(cmd) + print("Loaded Swift data formatters.") + + VIEWCFG_PATH = os.path.join(SWIFT_REPO, "utils", "viewcfg") BLOCKIFYASM_PATH = os.path.join(SWIFT_REPO, "utils", "dev-scripts", "blockifyasm") @@ -107,6 +118,7 @@ def sequence(debugger, command, exec_ctx, result, internal_dict): def __lldb_init_module(debugger, internal_dict): import_llvm_dataformatters(debugger) + import_swift_dataformatters(debugger) debugger.HandleCommand('command script add disassemble-asm-cfg ' '-f lldbToolBox.disassemble_asm_cfg') debugger.HandleCommand('command script add disassemble-to-file ' diff --git a/utils/run-test b/utils/run-test index e4bce782f9f41..c7db643dd5645 100755 --- a/utils/run-test +++ b/utils/run-test @@ -47,8 +47,19 @@ SWIFT_SOURCE_DIR = os.path.join(SWIFT_SOURCE_ROOT, 'swift') TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'test') VALIDATION_TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'validation-test') -LIT_BIN_DEFAULT = os.path.join(SWIFT_SOURCE_ROOT, 'llvm', + +def _get_default_llvm_source_dir(): + legacy_llvm_dir_path = os.path.join(SWIFT_SOURCE_ROOT, 'llvm') + if os.path.isdir(legacy_llvm_dir_path): + return legacy_llvm_dir_path + return os.path.join(SWIFT_SOURCE_ROOT, 'llvm-project', 'llvm') + + +# Default path for "lit.py" executable. +LIT_BIN_DEFAULT = os.path.join(os.environ.get("LLVM_SOURCE_DIR", + _get_default_llvm_source_dir()), 'utils', 'lit', 'lit.py') + host_target = StdlibDeploymentTarget.host_target().name diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index e5316b043eff8..8a277c58b7657 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -191,6 +191,8 @@ def __platforms_to_skip_build(self, args): StdlibDeploymentTarget.AppleWatchSimulator) if not args.build_android: platforms_to_skip_build.add(StdlibDeploymentTarget.Android) + if not args.build_wasm: + platforms_to_skip_build.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_build def __platforms_to_skip_test(self, args): @@ -230,6 +232,8 @@ def __platforms_to_skip_test(self, args): StdlibDeploymentTarget.AppleWatchSimulator) if not args.test_android: platforms_to_skip_test.add(StdlibDeploymentTarget.Android) + if not args.test_wasm: + platforms_to_skip_test.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_test @@ -250,4 +254,6 @@ def __platforms_to_skip_test_host(self, args): platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleTV) if not args.test_watchos_host: platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleWatch) + if not args.test_wasm_host: + platforms_to_skip_test_host.add(StdlibDeploymentTarget.WASI) return platforms_to_skip_test_host diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index 475483618f126..6a6850e53501b 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -30,7 +30,7 @@ def should_build(self, host_target): def run_bootstrap_script(self, action, host_target, additional_params=[]): script_path = os.path.join( - self.source_dir, 'Utilities', 'new-bootstrap') + self.source_dir, 'Utilities', 'bootstrap') toolchain_path = self.install_toolchain_path() swiftc = os.path.join(toolchain_path, "usr", "bin", "swiftc") @@ -58,7 +58,7 @@ def run_bootstrap_script(self, action, host_target, additional_params=[]): shell.call(helper_cmd) def build(self, host_target): - self.run_bootstrap_script('build', host_target) + self.run_bootstrap_script('build', host_target, ["--reconfigure"]) def should_test(self, host_target): return self.args.test_swiftpm diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 5e4f30ef1940d..f1cffa21f627a 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -158,6 +158,8 @@ class StdlibDeploymentTarget(object): Haiku = Platform("haiku", archs=["x86_64"]) + WASI = Platform("wasi", archs=["wasm32"]) + # The list of known platforms. known_platforms = [ OSX, @@ -169,7 +171,8 @@ class StdlibDeploymentTarget(object): Cygwin, Android, Windows, - Haiku] + Haiku, + WASI] # Cache of targets by name. _targets_by_name = dict((target.name, target) @@ -233,6 +236,10 @@ def host_target(): if machine == 'x86_64': return StdlibDeploymentTarget.Haiku.x86_64 + elif system == 'WASI': + if machine == 'wasm32': + return StdlibDeploymentTarget.WASI.wasm32 + raise NotImplementedError('System "%s" with architecture "%s" is not ' 'supported' % (system, machine)) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 071e45d4ab152..9c10c92c79b34 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -3,7 +3,7 @@ "https-clone-pattern": "https://github.com/%s.git", "repos" : { "swift": { - "remote": { "id": "apple/swift" } }, + "remote": { "id": "swiftwasm/swift" } }, "cmark": { "remote": { "id": "apple/swift-cmark" } }, "llbuild": { @@ -29,8 +29,7 @@ "ninja": { "remote": { "id": "ninja-build/ninja" } }, "icu": { - "remote": { "id": "unicode-org/icu" }, - "platforms": [ "Linux" ] + "remote": { "id": "unicode-org/icu" } }, "cmake": { "remote": { "id": "KitWare/CMake" }, @@ -43,11 +42,34 @@ "swift-format": { "remote": { "id": "apple/swift-format" } }, "llvm-project": { - "remote": { "id": "apple/llvm-project" } } + "remote": { "id": "swiftwasm/llvm-project" } } }, - "default-branch-scheme": "master", + "default-branch-scheme": "wasm", "branch-schemes": { - "master": { + "wasm": { + "aliases": ["wasm"], + "repos": { + "llvm-project": "swiftwasm", + "swift": "swiftwasm", + "cmark": "master", + "llbuild": "master", + "swiftpm": "master", + "swift-syntax": "master", + "swift-stress-tester": "master", + "swift-corelibs-xctest": "master", + "swift-corelibs-foundation": "master", + "swift-corelibs-libdispatch": "master", + "swift-integration-tests": "master", + "swift-xcode-playground-support": "master", + "ninja": "release", + "icu": "release-61-1", + "cmake": "v3.15.1", + "indexstore-db": "master", + "sourcekit-lsp": "master", + "swift-format": "master" + } + }, + "master": { "aliases": ["master", "swift/master"], "repos": { "llvm-project": "swift/master", @@ -228,7 +250,7 @@ "repos": { "llvm-project": "swift/swift-5.1-branch", "swift": "swift-5.1-branch", - "cmark": "master", + "cmark": "swift-5.1-branch", "llbuild": "swift-5.1-branch", "swiftpm": "swift-5.1-branch", "swift-syntax": "swift-5.1-branch", @@ -268,6 +290,30 @@ "sourcekit-lsp": "swift-5.2-branch", "swift-format": "master" } + }, + "master-rebranch": { + "aliases": ["master-rebranch", "swift/master-rebranch"], + "repos": { + "llvm-project": "swift/master-rebranch", + "swift": "master-rebranch", + "cmark": "master", + "llbuild": "master", + "swift-tools-support-core": "master", + "swiftpm": "master", + "swift-syntax": "master", + "swift-stress-tester": "master", + "swift-corelibs-xctest": "master", + "swift-corelibs-foundation": "master", + "swift-corelibs-libdispatch": "master", + "swift-integration-tests": "master", + "swift-xcode-playground-support": "master", + "ninja": "release", + "icu": "release-65-1", + "cmake": "v3.15.1", + "indexstore-db": "master", + "sourcekit-lsp": "master", + "swift-format": "master" + } } } } diff --git a/utils/webassembly/.gitignore b/utils/webassembly/.gitignore new file mode 100644 index 0000000000000..411f44532e7bc --- /dev/null +++ b/utils/webassembly/.gitignore @@ -0,0 +1,5 @@ +compiler +swiftwasm-sdk +prebuilt +output +tmpdir diff --git a/utils/webassembly/README.md b/utils/webassembly/README.md new file mode 100644 index 0000000000000..106f4fc35d84d --- /dev/null +++ b/utils/webassembly/README.md @@ -0,0 +1,23 @@ +Creates packages containing everything needed to build WebAssembly programs with Swift. + +# Building + +``` +./download-prebuilts.sh +./download-installable-prebuilts.sh +./build-packages.sh +``` + +# Contents of package + +- Swift toolchain from [swiftwasm-sdk](https://github.com/swiftwasm/swiftwasm-sdk) +- WASI modified sysroot from [wasi-sdk](https://github.com/swiftwasm/wasi-sdk) +- libicu from [icu4c-wasi](https://github.com/swiftwasm/icu4c-wasi) +- linking helpers from [swiftwasm-wasi-stubs](https://github.com/swiftwasm/swiftwasm-wasi-stubs) +- wasi-ld, either from wasi-sdk (on Linux) or upstream LLVM 9.0 (on Mac) +- build script for compiling a Swift file to a .wasm +- a Getting Started guide + +# Notes + +This shares a lot with the [swiftwasm-compile-service](https://github.com/swiftwasm/swiftwasm-compile-service). diff --git a/utils/webassembly/build-linux-package.sh b/utils/webassembly/build-linux-package.sh new file mode 100755 index 0000000000000..685d5c247a18e --- /dev/null +++ b/utils/webassembly/build-linux-package.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Unpacking Linux prebuilts" +mkdir -p output +cd linux +./unpack-prebuilts.sh +echo "Compressing" +tar cJf ../output/swiftwasm-sdk-linux.tar.xz swiftwasm-sdk +cd .. diff --git a/utils/webassembly/build-linux.sh b/utils/webassembly/build-linux.sh new file mode 100755 index 0000000000000..7aa8b90e26cfe --- /dev/null +++ b/utils/webassembly/build-linux.sh @@ -0,0 +1,35 @@ +#/bin/bash + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift + +$SWIFT_PATH/utils/build-script --wasm \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASI \ + -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ + -DSWIFT_SDKS='WASI;LINUX' \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DSWIFT_BUILD_SYNTAXPARSERLIB=FALSE \ + -DCMAKE_AR='$SOURCE_PATH/wasi-sdk/bin/llvm-ar' \ + -DCMAKE_RANLIB='$SOURCE_PATH/wasi-sdk/bin/llvm-ranlib' \ + " \ + --build-stdlib-deployment-targets "wasi-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-dynamic-stdlib false \ + --build-swift-static-sdk-overlay \ + --build-swift-static-stdlib \ + --install-destdir="$SOURCE_PATH/install" \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-swift \ + --installable-package="$SOURCE_PATH/swiftwasm-linux.tar.gz" \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasi-wasm32" \ + --wasi-icu-data "$SOURCE_PATH/icu_out/lib/libicudata.a" \ + --wasi-icu-i18n "$SOURCE_PATH/icu_out/lib/libicui18n.a" \ + --wasi-icu-i18n-include "$SOURCE_PATH/icu_out/include" \ + --wasi-icu-uc "$SOURCE_PATH/icu_out/lib/libicuuc.a" \ + --wasi-icu-uc-include "$SOURCE_PATH/icu_out/include" \ + --wasi-sdk "$SOURCE_PATH/wasi-sdk" \ + "$@" diff --git a/utils/webassembly/build-mac-package.sh b/utils/webassembly/build-mac-package.sh new file mode 100755 index 0000000000000..cd3c2602e8b5d --- /dev/null +++ b/utils/webassembly/build-mac-package.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Unpacking macOS prebuilts" +mkdir -p output +cd macos +./unpack-prebuilts.sh +echo "Compressing macOS package" +tar cJf ../output/swiftwasm-sdk-macos.tar.xz swiftwasm-sdk +cd .. diff --git a/utils/webassembly/build-mac.sh b/utils/webassembly/build-mac.sh new file mode 100755 index 0000000000000..1fef9d76ac8d6 --- /dev/null +++ b/utils/webassembly/build-mac.sh @@ -0,0 +1,35 @@ +#/bin/bash + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift + +$SWIFT_PATH/utils/build-script --wasm \ + --skip-build-benchmarks \ + --extra-cmake-options=" \ + -DSWIFT_PRIMARY_VARIANT_SDK:STRING=WASI \ + -DSWIFT_PRIMARY_VARIANT_ARCH:STRING=wasm32 \ + -DSWIFT_OSX_x86_64_ICU_STATICLIB=TRUE \ + -DSWIFT_BUILD_SOURCEKIT=FALSE \ + -DSWIFT_ENABLE_SOURCEKIT_TESTS=FALSE \ + -DSWIFT_BUILD_SYNTAXPARSERLIB=FALSE \ + -DCMAKE_AR='/usr/local/opt/llvm/bin/llvm-ar' \ + -DCMAKE_RANLIB='/usr/local/opt/llvm/bin/llvm-ranlib' \ + " \ + --build-stdlib-deployment-targets "wasi-wasm32" \ + --build-swift-dynamic-sdk-overlay false \ + --build-swift-dynamic-stdlib false \ + --build-swift-static-sdk-overlay \ + --build-swift-static-stdlib \ + --llvm-targets-to-build "X86;WebAssembly" \ + --stdlib-deployment-targets "wasi-wasm32" \ + --wasi-icu-data "$SOURCE_PATH/icu_out/lib/libicudata.a" \ + --wasi-icu-i18n "$SOURCE_PATH/icu_out/lib/libicui18n.a" \ + --wasi-icu-i18n-include "$SOURCE_PATH/icu_out/include" \ + --wasi-icu-uc "$SOURCE_PATH/icu_out/lib/libicuuc.a" \ + --wasi-icu-uc-include "$SOURCE_PATH/icu_out/include" \ + --wasi-sdk "$SOURCE_PATH/wasi-sdk" \ + --install-swift \ + --install-prefix="/opt/swiftwasm-sdk" \ + --install-destdir="$SOURCE_PATH/install" \ + --installable-package="$SOURCE_PATH/swiftwasm-macos.tar.gz" \ + "$@" diff --git a/utils/webassembly/build-packages.sh b/utils/webassembly/build-packages.sh new file mode 100755 index 0000000000000..e83fc38beb5d6 --- /dev/null +++ b/utils/webassembly/build-packages.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +rm -rf output || true +./build-linux-package.sh +./build-mac-package.sh diff --git a/utils/webassembly/ci-linux.sh b/utils/webassembly/ci-linux.sh new file mode 100755 index 0000000000000..8bfb2229a4574 --- /dev/null +++ b/utils/webassembly/ci-linux.sh @@ -0,0 +1,53 @@ +#/bin/bash + +sudo apt update +sudo apt install \ + git ninja-build clang python \ + uuid-dev libicu-dev icu-devtools libbsd-dev \ + libedit-dev libxml2-dev libsqlite3-dev swig \ + libpython-dev libncurses5-dev pkg-config \ + libblocksruntime-dev libcurl4-openssl-dev \ + systemtap-sdt-dev tzdata rsync wget + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift +BUILD_SCRIPT=$SWIFT_PATH/utils/webassembly/build-linux.sh +cd $SWIFT_PATH + +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha + +# Install wasmtime + +sudo mkdir /opt/wasmtime && cd /opt/wasmtime +wget -O - "https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-linux.tar.xz" | \ + sudo tar x --strip-components 1 +sudo ln -sf /opt/wasmtime/* /usr/local/bin + +cd $SOURCE_PATH + +wget -O install_cmake.sh "https://github.com/Kitware/CMake/releases/download/v3.15.3/cmake-3.15.3-Linux-x86_64.sh" +chmod +x install_cmake.sh +sudo mkdir -p /opt/cmake +sudo ./install_cmake.sh --skip-license --prefix=/opt/cmake +sudo ln -sf /opt/cmake/bin/* /usr/local/bin +cmake --version + +wget -O dist-wasi-sdk.tgz https://github.com/swiftwasm/wasi-sdk/suites/370986556/artifacts/809002 +unzip dist-wasi-sdk.tgz +WASI_SDK_TAR_PATH=$(find dist-ubuntu-latest.tgz -type f -name "wasi-sdk-*") +WASI_SDK_FULL_NAME=$(basename $WASI_SDK_TAR_PATH -linux.tar.gz) +tar xfz $WASI_SDK_TAR_PATH +mv $WASI_SDK_FULL_NAME ./wasi-sdk + +# Link wasm32-wasi-unknown to wasm32-wasi because clang finds crt1.o from sysroot +# with os and environment name `getMultiarchTriple`. +ln -s wasm32-wasi wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi-unknown + +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose +# Run test but ignore failure temporarily +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose -t || true diff --git a/utils/webassembly/ci-mac.sh b/utils/webassembly/ci-mac.sh new file mode 100755 index 0000000000000..a1b52e648da8e --- /dev/null +++ b/utils/webassembly/ci-mac.sh @@ -0,0 +1,43 @@ +#/bin/bash + +brew install cmake ninja llvm + +SOURCE_PATH="$( cd "$(dirname $0)/../../.." && pwd )" +SWIFT_PATH=$SOURCE_PATH/swift +BUILD_SCRIPT=$SWIFT_PATH/utils/webassembly/build-mac.sh +cd $SWIFT_PATH + +export current_sha=`git rev-parse HEAD` +./utils/update-checkout --clone --scheme wasm +git checkout $current_sha + +# Install wasmtime + +sudo mkdir /opt/wasmtime && cd /opt/wasmtime +wget -O - "https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-macos.tar.xz" | \ + sudo tar x --strip-components 1 +sudo ln -sf /opt/wasmtime/* /usr/local/bin + +cd $SOURCE_PATH + +wget -O dist-wasi-sdk.tgz https://github.com/swiftwasm/wasi-sdk/suites/370986556/artifacts/809001 +tar xfz dist-wasi-sdk.tgz +WASI_SDK_TAR_PATH=$(find dist-macos-latest.tgz -type f -name "wasi-sdk-*") +WASI_SDK_FULL_NAME=$(basename $WASI_SDK_TAR_PATH -macos.tar.gz) +tar xfz $WASI_SDK_TAR_PATH +mv $WASI_SDK_FULL_NAME ./wasi-sdk + +# Link sysroot/usr/include to sysroot/include because Darwin sysroot doesn't +# find header files in sysroot/include but sysroot/usr/include +mkdir wasi-sdk/share/wasi-sysroot/usr/ +ln -s ../include wasi-sdk/share/wasi-sysroot/usr/include +# Link wasm32-wasi-unknown to wasm32-wasi because clang finds crt1.o from sysroot +# with os and environment name `getMultiarchTriple`. +ln -s wasm32-wasi wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi-unknown + +wget -O icu.tar.xz "https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz" +tar xf icu.tar.xz + +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose +# Run test but ignore failure temporarily +$BUILD_SCRIPT --release --debug-swift-stdlib --verbose -t || true diff --git a/utils/webassembly/copy-shared-files.sh b/utils/webassembly/copy-shared-files.sh new file mode 100755 index 0000000000000..daaf0c5d30f82 --- /dev/null +++ b/utils/webassembly/copy-shared-files.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +cp -r ../sdkroot/* compiler/ +cp ../linux/compiler/opt/swiftwasm-sdk/lib/swift/wasi/wasm32/glibc.modulemap compiler/extra_utils diff --git a/utils/webassembly/download-installable-prebuilts.sh b/utils/webassembly/download-installable-prebuilts.sh new file mode 100755 index 0000000000000..c407580547fc4 --- /dev/null +++ b/utils/webassembly/download-installable-prebuilts.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +mkdir -p prebuilt +cd prebuilt +wget -O swiftwasm-linux.tar.gz https://github.com/swiftwasm/swiftwasm-sdk/releases/download/20191112.1.linux/swiftwasm.tar.gz +# Mac specific +wget -O swiftwasm-macos.tar.gz https://github.com/swiftwasm/swiftwasm-sdk/releases/download/20191112.1.mac/swiftwasm-mac.tar.gz diff --git a/utils/webassembly/download-prebuilts.sh b/utils/webassembly/download-prebuilts.sh new file mode 100755 index 0000000000000..5ee1c8b3bf756 --- /dev/null +++ b/utils/webassembly/download-prebuilts.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +mkdir -p prebuilt +cd prebuilt +wget https://github.com/swiftwasm/wasi-sdk/releases/download/20191022.1/wasi-sdk-4.39g3025a5f47c04-linux.tar.gz +wget https://github.com/swiftwasm/icu4c-wasi/releases/download/20190421.3/icu4c-wasi.tar.xz +# Mac specific +wget http://releases.llvm.org/9.0.0/clang+llvm-9.0.0-x86_64-darwin-apple.tar.xz diff --git a/utils/webassembly/linux/unpack-prebuilts.sh b/utils/webassembly/linux/unpack-prebuilts.sh new file mode 100755 index 0000000000000..a5d5f669aa39f --- /dev/null +++ b/utils/webassembly/linux/unpack-prebuilts.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +rm -rf swiftwasm-sdk compiler +mkdir swiftwasm-sdk +ln -s swiftwasm-sdk compiler +cd compiler +untar="../../prebuilt/wasi-sdk-"*"-linux.tar.gz +../../prebuilt/swiftwasm-linux.tar.gz +../../prebuilt/icu4c-wasi.tar.xz" +for i in $untar +do + echo $i + tar xf $i +done +cd .. +mv "compiler/wasi-sdk-"* "compiler/wasi-sdk" +mv compiler/wasi-sdk/share/wasi-sysroot compiler/wasi-sdk/share/sysroot +../remove-swift-extra-files.sh || true +../remove-wasi-extra-files.sh || true +../copy-shared-files.sh || true diff --git a/utils/webassembly/macos/unpack-prebuilts.sh b/utils/webassembly/macos/unpack-prebuilts.sh new file mode 100755 index 0000000000000..199552346db2e --- /dev/null +++ b/utils/webassembly/macos/unpack-prebuilts.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e +rm -rf swiftwasm-sdk compiler tmpdir +mkdir swiftwasm-sdk tmpdir +ln -s swiftwasm-sdk compiler +cd compiler +untar="../../prebuilt/wasi-sdk-"*"-linux.tar.gz +../../prebuilt/swiftwasm-macos.tar.gz +../../prebuilt/icu4c-wasi.tar.xz" +for i in $untar +do + echo $i + tar xf $i +done +# Mac: unpack the Linux one and copy stdlibs +cd .. +cd tmpdir +tar xf ../../prebuilt/clang+llvm-*-x86_64-darwin-apple.tar.xz +tar xf ../../prebuilt/swiftwasm-linux.tar.gz +cd .. +mv "compiler/wasi-sdk-"* "compiler/wasi-sdk" +mv compiler/wasi-sdk/share/wasi-sysroot compiler/wasi-sdk/share/sysroot +../remove-swift-extra-files.sh || true +rm -r compiler/wasi-sdk/bin +mkdir compiler/wasi-sdk/bin +cp tmpdir/clang+llvm-*-x86_64-darwin-apple/bin/wasm-ld compiler/wasi-sdk/bin +cp -a tmpdir/opt/swiftwasm-sdk/lib/swift/wasi compiler/opt/swiftwasm-sdk/lib/swift/wasi +cp -a tmpdir/opt/swiftwasm-sdk/lib/swift_static compiler/opt/swiftwasm-sdk/lib/swift_static +# ok, finally copy over the shared files +../copy-shared-files.sh || true diff --git a/utils/webassembly/remove-swift-extra-files.sh b/utils/webassembly/remove-swift-extra-files.sh new file mode 100755 index 0000000000000..2e4e2d541a45f --- /dev/null +++ b/utils/webassembly/remove-swift-extra-files.sh @@ -0,0 +1,26 @@ +#!/bin/bash +basepath="compiler/opt/swiftwasm-sdk" +filestoremove="bin/sil-* +bin/lldb* +bin/sourcekitd-* +bin/swift-api-digester +bin/swift-autolink-extract +bin/swift-demangle +bin/swift-demangle-yamldump +bin/swift-format +bin/swift-llvm-opt +bin/swift-refactor +bin/swift-reflection-dump +bin/swift-*-test +lib/libsourcekitdInProc.so +lib/swift/clang/lib/linux/* +lib/swift_static/linux/* +lib/swift/linux/x86_64/* +lib/swift/linux/*" +for i in $filestoremove +do + echo $basepath/$i + rm $basepath/$i +done +# Mac only +rm -r $basepath/lib/swift/macosx $basepath/lib/sourcekitd.framework diff --git a/utils/webassembly/remove-wasi-extra-files.sh b/utils/webassembly/remove-wasi-extra-files.sh new file mode 100755 index 0000000000000..19ac34009874f --- /dev/null +++ b/utils/webassembly/remove-wasi-extra-files.sh @@ -0,0 +1,10 @@ +#!/bin/bash +basepath="compiler/wasi-sdk" +filestoremove="bin/clang* +bin/llvm* +bin/llc" +for i in $filestoremove +do + echo $basepath/$i + rm $basepath/$i +done diff --git a/utils/webassembly/sdkroot/README.md b/utils/webassembly/sdkroot/README.md new file mode 100644 index 0000000000000..db8048d2a504d --- /dev/null +++ b/utils/webassembly/sdkroot/README.md @@ -0,0 +1,87 @@ +SwiftWasm: Getting started +========================== + +Thank you for trying SwiftWasm! Here's how to get started. + +Please visit our website at https://swiftwasm.org for the latest updates. + + +Install dependencies +==================== + +Before running SwiftWasm, you will need to install some dependencies. + +Ubuntu: + +``` +sudo apt-get install libatomic1 +``` + +macOS: + +(No dependencies needed.) + +Windows: + +Install Windows Subsystem for Linux, then follow the Ubuntu instructions. + + + + +Compile SwiftWasm +================= + +Run + +``` +./swiftwasm example/hello.swift hello.wasm +``` + +To compile example/hello.swift to hello.wasm. + + + + +Running Wasm files +================== + +To run the resulting hello.wasm file: + +- Visit https://swiftwasm.org/polyfill/ +- select "Browse", and choose the hello.wasm file +- you should get output in the textbox. + +This polyfill should work in Firefox 66, Chrome 74, and Safari 12.1. + +You can also run the file outside a browser with: + +- Wasmtime https://github.com/CraneStation/wasmtime +- Lucet https://github.com/swiftwasm/lucet/tree/swiftwasm +- or any other WASI-compatible WebAssembly runtime. + + + + +Questions and support +===================== + +If you have any questions, please open an issue on + +https://github.com/swiftwasm/swift + + + +Third-party licenses +==================== + +This package contains components with their own license requirements. + +Swift compiler: https://github.com/apple/swift/blob/master/LICENSE.txt + +LLVM/Clang: https://github.com/llvm/llvm-project/blob/master/lld/LICENSE.TXT + +WASI sysroot: https://github.com/CraneStation/wasi-sysroot/blob/master/LICENSE + +ICU: https://github.com/unicode-org/icu/blob/master/icu4c/LICENSE + +WASI polyfill: https://github.com/CraneStation/wasmtime/blob/master/wasmtime-wasi/LICENSE diff --git a/utils/webassembly/sdkroot/example/hello.swift b/utils/webassembly/sdkroot/example/hello.swift new file mode 100644 index 0000000000000..e61506705879c --- /dev/null +++ b/utils/webassembly/sdkroot/example/hello.swift @@ -0,0 +1 @@ +print("Hello, 🌐!") diff --git a/utils/webassembly/sdkroot/extra_objs/fakelocaltime.o b/utils/webassembly/sdkroot/extra_objs/fakelocaltime.o new file mode 100644 index 0000000000000..904d3a6dedc61 Binary files /dev/null and b/utils/webassembly/sdkroot/extra_objs/fakelocaltime.o differ diff --git a/utils/webassembly/sdkroot/extra_objs/fakepthread.o b/utils/webassembly/sdkroot/extra_objs/fakepthread.o new file mode 100644 index 0000000000000..d95d0e77611b8 Binary files /dev/null and b/utils/webassembly/sdkroot/extra_objs/fakepthread.o differ diff --git a/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh b/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh new file mode 100755 index 0000000000000..940e5a413339b --- /dev/null +++ b/utils/webassembly/sdkroot/extra_utils/generateModulemap.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec sed -e "s@\"/include@\"$1/include@g" "$(dirname $0)/glibc.modulemap" diff --git a/utils/webassembly/sdkroot/swiftwasm b/utils/webassembly/sdkroot/swiftwasm new file mode 100755 index 0000000000000..278ce7dbc256a --- /dev/null +++ b/utils/webassembly/sdkroot/swiftwasm @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +if [ "$#" -lt 2 ] +then + echo "usage: swiftwasm file1.swift file2.swift.... output.wasm" + exit 1 +fi +sdk="$(dirname $0)" +tmpobj="$(mktemp -t swiftwasm-XXXXXXXX)" +outputfile="${@: -1}" +if [[ "$outputfile" != *.wasm ]] +then + echo "output should end in .wasm" + exit 1 +fi +sysroot="$(dirname $0)/wasi-sdk/share/sysroot" +abssysroot="$(cd "$(dirname "$sysroot")" && pwd)/$(basename "$sysroot")" + +"$sdk/extra_utils/generateModulemap.sh" "$abssysroot" >"$sdk/opt/swiftwasm-sdk/lib/swift/wasi/wasm32/glibc.modulemap" + +"$sdk/opt/swiftwasm-sdk/bin/swiftc" -target wasm32-unknown-unknown-wasi \ + -sdk "$sysroot" -O -c \ + -o "$tmpobj" \ + "${@:1:$#-1}" +"$sdk/wasi-sdk/bin/wasm-ld" --error-limit=0 -o "$outputfile" \ + "$sysroot/lib/wasm32-wasi/crt1.o" \ + "$sdk/opt/swiftwasm-sdk/lib/swift_static/wasi/wasm32/swiftrt.o" \ + "$tmpobj" \ + "-L$sdk/opt/swiftwasm-sdk/lib/swift_static/wasi" \ + "-L$sysroot/lib/wasm32-wasi" \ + "-L$sdk/icu_out/lib" \ + -lswiftCore \ + -lc -lc++ -lc++abi -lswiftImageInspectionShared \ + -licuuc -licudata \ + "$sdk/wasi-sdk/lib/clang/9.0.0/lib/wasi/libclang_rt.builtins-wasm32.a" \ + "$sdk/extra_objs/fakepthread.o" \ + --no-gc-sections \ + --no-threads +rm "$tmpobj" diff --git a/utils/webassembly/static-executable-args.lnk b/utils/webassembly/static-executable-args.lnk new file mode 100644 index 0000000000000..d80b4a58a272c --- /dev/null +++ b/utils/webassembly/static-executable-args.lnk @@ -0,0 +1,16 @@ +-static +-lswiftCore +-lswiftImageInspectionShared +-lswiftSwiftOnoneSupport +-lswiftWasiPthread +-licui18n +-licuuc +-licudata +-ldl +-lstdc++ +-lm +-lwasi-emulated-mman +-Xlinker --error-limit=0 +-Xlinker --no-gc-sections +-Xlinker --no-threads +-D_WASI_EMULATED_MMAN diff --git a/utils/webassembly/static-stdlib-args.lnk b/utils/webassembly/static-stdlib-args.lnk new file mode 100644 index 0000000000000..af390c6413bd2 --- /dev/null +++ b/utils/webassembly/static-stdlib-args.lnk @@ -0,0 +1,15 @@ +-ldl +-latomic +-lswiftWasiPthread +-lswiftCore +-latomic +-lswiftImageInspectionShared +-licui18n +-licuuc +-licudata +-lstdc++ +-lm +-Xlinker +--exclude-libs +-Xlinker +ALL diff --git a/validation-test/Driver/batch_mode_size_limit.swift b/validation-test/Driver/batch_mode_size_limit.swift index 0c55c326dacee..4ca7918958460 100644 --- a/validation-test/Driver/batch_mode_size_limit.swift +++ b/validation-test/Driver/batch_mode_size_limit.swift @@ -1,3 +1,5 @@ +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + // RUN: %empty-directory(%t) // RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift // RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift @@ -9,26 +11,66 @@ // RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift // RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift // RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift -// RUN: %swiftc_driver -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -// CHECK-NOT: unable to execute command -// CHECK: Forming into 4 batches -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 + +// RUN: %FileCheck %s -check-prefix=CHECK-DIS <%t/out.txt +// CHECK-DIS-NOT: unable to execute command +// CHECK-DIS: Forming into 4 batches +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents + + +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-DIS +// EXPLICIT-ARG-DIS-NOT: unable to execute command +// EXPLICIT-ARG-DIS: Forming into 10 batches +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents + + +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + +// RUN: %empty-directory(%t) +// RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift +// RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift +// RUN: touch %t/f_3_1.swift %t/f_3_2.swift %t/f_3_3.swift %t/f_3_4.swift %t/f_3_5.swift %t/f_3_6.swift %t/f_3_7.swift %t/f_3_8.swift %t/f_3_9.swift %t/f_3_10.swift +// RUN: touch %t/f_4_1.swift %t/f_4_2.swift %t/f_4_3.swift %t/f_4_4.swift %t/f_4_5.swift %t/f_4_6.swift %t/f_4_7.swift %t/f_4_8.swift %t/f_4_9.swift %t/f_4_10.swift +// RUN: touch %t/f_5_1.swift %t/f_5_2.swift %t/f_5_3.swift %t/f_5_4.swift %t/f_5_5.swift %t/f_5_6.swift %t/f_5_7.swift %t/f_5_8.swift %t/f_5_9.swift %t/f_5_10.swift +// RUN: touch %t/f_6_1.swift %t/f_6_2.swift %t/f_6_3.swift %t/f_6_4.swift %t/f_6_5.swift %t/f_6_6.swift %t/f_6_7.swift %t/f_6_8.swift %t/f_6_9.swift %t/f_6_10.swift +// RUN: touch %t/f_7_1.swift %t/f_7_2.swift %t/f_7_3.swift %t/f_7_4.swift %t/f_7_5.swift %t/f_7_6.swift %t/f_7_7.swift %t/f_7_8.swift %t/f_7_9.swift %t/f_7_10.swift +// RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift +// RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift +// RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 +// RUN: %FileCheck %s -check-prefix=CHECK-ENA <%t/out.txt +// CHECK-ENA-NOT: unable to execute command +// CHECK-ENA: Forming into 4 batches +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 24 constituents // -// RUN: %swiftc_driver -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -check-prefix=EXPLICIT-ARG -// EXPLICIT-ARG-NOT: unable to execute command -// EXPLICIT-ARG: Forming into 10 batches -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-ENA +// EXPLICIT-ARG-ENA-NOT: unable to execute command +// EXPLICIT-ARG-ENA: Forming into 10 batches +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 9 constituents diff --git a/validation-test/IDE/crashers_2_fixed/rdar52105899.swift b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift new file mode 100644 index 0000000000000..cbce5cd0c6d6e --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: echo '//DUMMY' > %t/dummy.swift +// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s %t/dummy.swift > /dev/null +// RUN: %target-swift-ide-test -code-completion -code-completion-token=B -source-filename=%s %t/dummy.swift > /dev/null + +struct MyStruct { + let _: Int = #^A^# +} + +let _: Int = #^B^# diff --git a/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift new file mode 100644 index 0000000000000..9a6faf06360c2 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_254CaseNoPayloads.swift @@ -0,0 +1,365 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_254CaseNoPayloads +// RUN: %target-codesign %t/reflect_Enum_254CaseNoPayloads + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_254CaseNoPayloads | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib + +import SwiftReflectionTest + +struct Marker { + let value = 1 + let extra = 2 +} + +enum E254CaseNoPayloadsEnum { +case option0 +case option1 +case option2 +case option3 +case option4 +case option5 +case option6 +case option7 +case option8 +case option9 +case option10 +case option11 +case option12 +case option13 +case option14 +case option15 +case option16 +case option17 +case option18 +case option19 +case option20 +case option21 +case option22 +case option23 +case option24 +case option25 +case option26 +case option27 +case option28 +case option29 +case option30 +case option31 +case option32 +case option33 +case option34 +case option35 +case option36 +case option37 +case option38 +case option39 +case option40 +case option41 +case option42 +case option43 +case option44 +case option45 +case option46 +case option47 +case option48 +case option49 +case option50 +case option51 +case option52 +case option53 +case option54 +case option55 +case option56 +case option57 +case option58 +case option59 +case option60 +case option61 +case option62 +case option63 +case option64 +case option65 +case option66 +case option67 +case option68 +case option69 +case option70 +case option71 +case option72 +case option73 +case option74 +case option75 +case option76 +case option77 +case option78 +case option79 +case option80 +case option81 +case option82 +case option83 +case option84 +case option85 +case option86 +case option87 +case option88 +case option89 +case option90 +case option91 +case option92 +case option93 +case option94 +case option95 +case option96 +case option97 +case option98 +case option99 +case option100 +case option101 +case option102 +case option103 +case option104 +case option105 +case option106 +case option107 +case option108 +case option109 +case option110 +case option111 +case option112 +case option113 +case option114 +case option115 +case option116 +case option117 +case option118 +case option119 +case option120 +case option121 +case option122 +case option123 +case option124 +case option125 +case option126 +case option127 +case option128 +case option129 +case option130 +case option131 +case option132 +case option133 +case option134 +case option135 +case option136 +case option137 +case option138 +case option139 +case option140 +case option141 +case option142 +case option143 +case option144 +case option145 +case option146 +case option147 +case option148 +case option149 +case option150 +case option151 +case option152 +case option153 +case option154 +case option155 +case option156 +case option157 +case option158 +case option159 +case option160 +case option161 +case option162 +case option163 +case option164 +case option165 +case option166 +case option167 +case option168 +case option169 +case option170 +case option171 +case option172 +case option173 +case option174 +case option175 +case option176 +case option177 +case option178 +case option179 +case option180 +case option181 +case option182 +case option183 +case option184 +case option185 +case option186 +case option187 +case option188 +case option189 +case option190 +case option191 +case option192 +case option193 +case option194 +case option195 +case option196 +case option197 +case option198 +case option199 +case option200 +case option201 +case option202 +case option203 +case option204 +case option205 +case option206 +case option207 +case option208 +case option209 +case option210 +case option211 +case option212 +case option213 +case option214 +case option215 +case option216 +case option217 +case option218 +case option219 +case option220 +case option221 +case option222 +case option223 +case option224 +case option225 +case option226 +case option227 +case option228 +case option229 +case option230 +case option231 +case option232 +case option233 +case option234 +case option235 +case option236 +case option237 +case option238 +case option239 +case option240 +case option241 +case option242 +case option243 +case option244 +case option245 +case option246 +case option247 +case option248 +case option249 +case option250 +case option251 +case option252 +case option253 +} + +class ClassWith254CaseEnum { + var e1: E254CaseNoPayloadsEnum = .option0 + var e2: E254CaseNoPayloadsEnum? + var e3: E254CaseNoPayloadsEnum?? + var e4: E254CaseNoPayloadsEnum??? + var e5: E254CaseNoPayloadsEnum???? +} + +reflect(object: ClassWith254CaseEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_254CaseNoPayloads.ClassWith254CaseEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=24 alignment=1 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))) +// CHECK-64: (field name=e3 offset=18 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))) +// CHECK-64: (field name=e4 offset=19 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))))) +// CHECK-64: (field name=e5 offset=21 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_254CaseNoPayloads.ClassWith254CaseEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=16 alignment=1 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))) +// CHECK-32: (field name=e3 offset=10 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))) +// CHECK-32: (field name=e4 offset=11 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1)))))))) +// CHECK-32: (field name=e5 offset=13 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=1 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=2 bitwise_takable=1))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_NoCase.swift b/validation-test/Reflection/reflect_Enum_NoCase.swift new file mode 100644 index 0000000000000..537750d6308d3 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_NoCase.swift @@ -0,0 +1,125 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_NoCase +// RUN: %target-codesign %t/reflect_Enum_NoCase + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_NoCase | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +enum NoCaseEnum { +} + +class ClassWithNoCaseEnum { + var e1: NoCaseEnum? + var e2: NoCaseEnum?? + var e3: NoCaseEnum??? + var e4: NoCaseEnum???? + var e5: NoCaseEnum????? +} + +reflect(object: ClassWithNoCaseEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_NoCase.ClassWithNoCaseEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=31 alignment=1 stride=31 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e3 offset=19 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e4 offset=22 +// CHECK-64: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e5 offset=26 +// CHECK-64: (single_payload_enum size=5 alignment=1 stride=5 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_NoCase.ClassWithNoCaseEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=23 alignment=1 stride=23 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e3 offset=11 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e4 offset=14 +// CHECK-32: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e5 offset=18 +// CHECK-32: (single_payload_enum size=5 alignment=1 stride=5 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=4 alignment=1 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=3 alignment=1 stride=3 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift b/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift new file mode 100644 index 0000000000000..98a61263b3b69 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_SingleCaseNoPayload.swift @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_SingleCaseNoPayload +// RUN: %target-codesign %t/reflect_Enum_SingleCaseNoPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_SingleCaseNoPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum SingleCaseNoPayloadEnum { +case `default` +} + +class ClassWithSingleCaseNoPayloadEnum { + var e1: SingleCaseNoPayloadEnum? + var e2: SingleCaseNoPayloadEnum = .`default` + var e3: SingleCaseNoPayloadEnum? = .`default` + var e4: SingleCaseNoPayloadEnum?? + let marker = Marker() + +} + +reflect(object: ClassWithSingleCaseNoPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_SingleCaseNoPayload.ClassWithSingleCaseNoPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=32 alignment=8 stride=32 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=e3 offset=17 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=e4 offset=18 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=marker offset=24 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_SingleCaseNoPayload.ClassWithSingleCaseNoPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=16 alignment=4 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=e3 offset=9 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=e4 offset=10 +// CHECK-32: (single_payload_enum size=2 alignment=1 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=marker offset=12 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift b/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift new file mode 100644 index 0000000000000..5cac117d30283 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_SingleCasePointerPayload.swift @@ -0,0 +1,81 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_SingleCasePointerPayload +// RUN: %target-codesign %t/reflect_Enum_SingleCasePointerPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_SingleCasePointerPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +class Marker { + let value = 1 +} + +enum SingleCasePointerPayloadEnum { +case only(Marker) +} + +class ClassWithSingleCasePointerPayloadEnum { + var e1: SingleCasePointerPayloadEnum? + var e2: SingleCasePointerPayloadEnum = .only(Marker()) + var e3: SingleCasePointerPayloadEnum? = .some(.only(Marker())) + var e4: SingleCasePointerPayloadEnum?? +} + +reflect(object: ClassWithSingleCasePointerPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_SingleCasePointerPayload.ClassWithSingleCasePointerPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=48 alignment=8 stride=48 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native)))) +// CHECK-64: (field name=e2 offset=24 +// CHECK-64: (reference kind=strong refcounting=native)) +// CHECK-64: (field name=e3 offset=32 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native)))) +// CHECK-64: (field name=e4 offset=40 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483645 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=2147483646 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (reference kind=strong refcounting=native))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_SingleCasePointerPayload.ClassWithSingleCasePointerPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=24 alignment=4 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native)))) +// CHECK-32: (field name=e2 offset=12 +// CHECK-32: (reference kind=strong refcounting=native)) +// CHECK-32: (field name=e3 offset=16 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native)))) +// CHECK-32: (field name=e4 offset=20 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4094 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (reference kind=strong refcounting=native))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift new file mode 100644 index 0000000000000..219eeee88fda1 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseNoPayload.swift @@ -0,0 +1,98 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseNoPayload +// RUN: %target-codesign %t/reflect_Enum_TwoCaseNoPayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseNoPayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum TwoCaseNoPayloadEnum { +case preferred +case other +} + +class ClassWithTwoCaseNoPayloadEnum { + var e1: TwoCaseNoPayloadEnum? + var e2: TwoCaseNoPayloadEnum = .preferred + var e3: TwoCaseNoPayloadEnum = .other + var e4: TwoCaseNoPayloadEnum? = .preferred + var e5: TwoCaseNoPayloadEnum? = .other + let marker = Marker() + +} + +reflect(object: ClassWithTwoCaseNoPayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseNoPayload.ClassWithTwoCaseNoPayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=32 alignment=8 stride=32 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=e2 offset=17 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-64: (field name=e3 offset=18 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-64: (field name=e4 offset=19 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=e5 offset=20 +// CHECK-64: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-64: (field name=marker offset=24 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseNoPayload.ClassWithTwoCaseNoPayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=20 alignment=4 stride=20 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=e2 offset=9 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-32: (field name=e3 offset=10 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)) +// CHECK-32: (field name=e4 offset=11 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=e5 offset=12 +// CHECK-32: (single_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=254 bitwise_takable=1)))) +// CHECK-32: (field name=marker offset=16 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift b/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift new file mode 100644 index 0000000000000..e3b83de75c057 --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseOnePayload.swift @@ -0,0 +1,168 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseOnePayload +// RUN: %target-codesign %t/reflect_Enum_TwoCaseOnePayload + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseOnePayload | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 +} + +enum TwoCaseOnePayloadEnum { +case valid(Marker) +case invalid +} + +class ClassWithTwoCaseOnePayloadEnum { + var e1: TwoCaseOnePayloadEnum? + var e2: TwoCaseOnePayloadEnum = .valid(Marker()) + var e3: TwoCaseOnePayloadEnum = .invalid + var e4: TwoCaseOnePayloadEnum? = .valid(Marker()) + var e5: TwoCaseOnePayloadEnum? = .invalid + var e6: TwoCaseOnePayloadEnum?? +} + +reflect(object: ClassWithTwoCaseOnePayloadEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseOnePayload.ClassWithTwoCaseOnePayloadEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=107 alignment=8 stride=112 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e2 offset=32 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e3 offset=48 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e4 offset=64 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e5 offset=80 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-64: (field name=e6 offset=96 +// CHECK-64: (single_payload_enum size=11 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseOnePayload.ClassWithTwoCaseOnePayloadEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=55 alignment=4 stride=56 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e2 offset=16 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e3 offset=24 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e4 offset=32 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e5 offset=40 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))))) +// CHECK-32: (field name=e6 offset=48 +// CHECK-32: (single_payload_enum size=7 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=6 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift b/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift new file mode 100644 index 0000000000000..dd640eb450c1b --- /dev/null +++ b/validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift @@ -0,0 +1,265 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -g -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_TwoCaseTwoPayloads +// RUN: %target-codesign %t/reflect_Enum_TwoCaseTwoPayloads + +// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_TwoCaseTwoPayloads | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +struct Marker { + let value = 1 + let extra = 2 +} + +enum TwoCaseTwoPayloadsEnum { +case valid(Marker) +case invalid(Int) +} + +class ClassWithTwoCaseTwoPayloadsEnum { + var e1: TwoCaseTwoPayloadsEnum? + var e2: TwoCaseTwoPayloadsEnum = .valid(Marker()) + var e3: TwoCaseTwoPayloadsEnum = .invalid(7) + var e4: TwoCaseTwoPayloadsEnum? = .valid(Marker()) + var e5: TwoCaseTwoPayloadsEnum? = .invalid(7) + var e6: TwoCaseTwoPayloadsEnum?? +} + +reflect(object: ClassWithTwoCaseTwoPayloadsEnum()) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_Enum_TwoCaseTwoPayloads.ClassWithTwoCaseTwoPayloadsEnum) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=153 alignment=8 stride=160 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=e1 offset=16 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e2 offset=40 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e3 offset=64 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=e4 offset=88 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e5 offset=112 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-64: (field name=e6 offset=136 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=252 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (single_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-64: (field name=some offset=0 +// CHECK-64: (multi_payload_enum size=17 alignment=8 stride=24 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-64: (field name=valid offset=0 +// CHECK-64: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=value offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=extra offset=8 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64: (field name=invalid offset=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_Enum_TwoCaseTwoPayloads.ClassWithTwoCaseTwoPayloadsEnum) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=77 alignment=4 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=e1 offset=8 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e2 offset=20 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e3 offset=32 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=e4 offset=44 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e5 offset=56 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))))) +// CHECK-32: (field name=e6 offset=68 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=252 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (single_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=253 bitwise_takable=1 +// CHECK-32: (field name=some offset=0 +// CHECK-32: (multi_payload_enum size=9 alignment=4 stride=12 num_extra_inhabitants=254 bitwise_takable=1 +// CHECK-32: (field name=valid offset=0 +// CHECK-32: (struct size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=value offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=extra offset=4 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32: (field name=invalid offset=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_empty_struct.swift b/validation-test/Reflection/reflect_empty_struct.swift index 32427344a3fc9..df2e365172082 100644 --- a/validation-test/Reflection/reflect_empty_struct.swift +++ b/validation-test/Reflection/reflect_empty_struct.swift @@ -1,11 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.99.0 // RUN: %target-codesign %t/reflect_empty_struct // RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail // REQUIRES: objc_interop // REQUIRES: executable_test +// REQUIRES: OS=macosx import SwiftReflectionTest diff --git a/validation-test/Reflection/reflect_empty_struct_compat.swift b/validation-test/Reflection/reflect_empty_struct_compat.swift new file mode 100644 index 0000000000000..93188e9f1010e --- /dev/null +++ b/validation-test/Reflection/reflect_empty_struct_compat.swift @@ -0,0 +1,75 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.15.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.14.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// REQUIRES: objc_interop +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import SwiftReflectionTest + +import EmptyStruct + +@_alignment(1) struct EmptyStruct { } +class Class { + var a = EmptyStruct() + var b: Any = EmptyStruct() + var c = EmptyStructC() + var d: Any = EmptyStructC() +} + +var obj = Class() + +reflect(object: obj) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_empty_struct.Class) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=a offset=16 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=b offset=16 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))) +// CHECK-64: (field name=c offset=48 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=d offset=48 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_empty_struct.Class) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=a offset=8 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=b offset=8 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))) +// CHECK-32: (field name=c offset=24 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=d offset=24 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Sema/SwiftUI/rdar57201781.swift b/validation-test/Sema/SwiftUI/rdar57201781.swift index 5591619ced6cc..be35137e218a8 100644 --- a/validation-test/Sema/SwiftUI/rdar57201781.swift +++ b/validation-test/Sema/SwiftUI/rdar57201781.swift @@ -8,7 +8,7 @@ struct ContentView : View { @State var foo: [String] = Array(repeating: "", count: 5) var body: some View { - VStack { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} + VStack { // expected-error {{expression type 'VStack<_>' is ambiguous without more context}} HStack { Text("") TextFi // expected-error {{use of unresolved identifier 'TextFi'}} diff --git a/validation-test/Sema/generic_function_signature.swift b/validation-test/Sema/generic_function_signature.swift new file mode 100644 index 0000000000000..92eee65355e4e --- /dev/null +++ b/validation-test/Sema/generic_function_signature.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -typecheck %s +enum E {} + +protocol P { + associatedtype T + var closure: () -> E { get } +} + +struct Concrete: P { + let closure: () -> E +} diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 2e6dfe51e708f..d29e72ce83713 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -28,7 +28,6 @@ protocol P { extension P { static func ≈> (f: Self, b: @escaping (inout B) -> R) -> M {} - // expected-note@-1 {{in call to operator '≈>'}} } extension WritableKeyPath : P { @@ -43,5 +42,5 @@ extension WritableKeyPath : P { struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } -// expected-error@-1 {{generic parameter 'R' could not be inferred}} +// expected-error@-1 {{unable to infer complex closure return type; add explicit type to disambiguate}} // expected-error@-2 {{cannot convert value of type 'M, R>' to expected argument type 'WritableKeyPath<_, _>'}} diff --git a/validation-test/compiler_crashers_2_fixed/sr11939.swift b/validation-test/compiler_crashers_2_fixed/sr11939.swift new file mode 100644 index 0000000000000..1d097021efc2e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11939.swift @@ -0,0 +1,4 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +func sr_11939(_ closure: @autoclosure () -> String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} +sr_11939("A") // No crash diff --git a/validation-test/execution/dsohandle-multi-module.swift b/validation-test/execution/dsohandle-multi-module.swift index 01a28856bcf6c..734c599f0d318 100644 --- a/validation-test/execution/dsohandle-multi-module.swift +++ b/validation-test/execution/dsohandle-multi-module.swift @@ -9,6 +9,7 @@ // REQUIRES: executable_test // UNSUPPORTED: linux +// XFAIL: windows import first import second diff --git a/validation-test/stdlib/Slice.swift.gyb b/validation-test/stdlib/Slice.swift.gyb index 3752fc6e67801..58b01fcd54602 100644 --- a/validation-test/stdlib/Slice.swift.gyb +++ b/validation-test/stdlib/Slice.swift.gyb @@ -113,30 +113,35 @@ SliceTests.test("${Collection}.Slice.{startIndex,endIndex}") { % end % end +SliceTests.test("${Collection}.Slice.withContiguousStorageIfAvailable") { + for test in subscriptRangeTests { + let c = ${Collection}(elements: test.collection) + let bounds = test.bounds(in: c) + var slice = Slice(base: c, bounds: bounds) + let r = slice.withContiguousStorageIfAvailable { _ in } + expectNil(r) // None of the minimal collection types implement this. + } +} + //===----------------------------------------------------------------------===// // MutableSlice //===----------------------------------------------------------------------===// -/* -FIXME: uncomment this test when the following bug is fixed: - - Recast Slice and MutableSlice in terms of Collection -and MutableCollection - -extension MutableSlice { +extension Slice { func _checkBaseSubSequenceElementIsElement() { + expectEqualType( Element.self, Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, Base.Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, + Iterator.Element.self) + expectEqualType( + Element.self, Base.SubSequence.Iterator.Element.self) } } -*/ runAllTests() diff --git a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb index 6a52caa22417c..2ed995338afec 100644 --- a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb +++ b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb @@ -63,7 +63,7 @@ ${SelfName}TestSuite.test("AssociatedTypes") { % if IsRaw: iteratorType: ${SelfType}.Iterator.self, % else: - iteratorType: UnsafeBufferPointerIterator.self, + iteratorType: UnsafeBufferPointer.Iterator.self, % end subSequenceType: Slice<${SelfType}>.self, indexType: Int.self, @@ -361,6 +361,31 @@ UnsafeMutableBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvaila expectEqual(result1, result2) } +UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable") { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { + return + } + + for test in subscriptRangeTests { + let c = test.collection.map { MinimalEquatableValue($0.value) } + let buffer = UnsafeMutableBufferPointer.allocate( + capacity: test.collection.count) + var (it, idx) = buffer.initialize(from: c) + expectEqual(buffer.endIndex, idx) + expectNil(it.next()) + + let expected = test.expected.map { MinimalEquatableValue($0.value) } + let r1: Void? = buffer[test.bounds].withContiguousStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r1) + let r2: Void? = buffer[test.bounds].withContiguousMutableStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r2) + } +} + UnsafeMutableBufferPointerTestSuite.test("sort") { var values = (0..<1000).map({ _ in Int.random(in: 0..<100) }) let sortedValues = values.sorted() @@ -389,7 +414,7 @@ for testIndex in (0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testIndex < sliceRange.lowerBound || @@ -430,7 +455,7 @@ for testRange in testRanges { % Type = 'Unsafe' + ('Mutable' if mutable else '') + ('Raw' if raw else '') + 'BufferPointer' ${Type}TestSuite.test("${action}Slice\(testRange)ViaSlice") { % if raw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: bufCount+2, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: bufCount+2, alignment: 8) for i in 0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testRange.lowerBound < sliceRange.lowerBound || @@ -937,7 +962,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${R UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set/overlaps") { % if IsRaw: - let buffer = UnsafeMutableRawBufferPointer.allocate(count: 4) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 4, alignment: 1) % else: let buffer = UnsafeMutableBufferPointer.allocate(capacity: 4) % end @@ -970,7 +995,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("nonmutating-swapAt") { % if IsRaw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: 4, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 8) let buffer = UnsafeMutableRawBufferPointer(start: allocated, count: 3) allocated.storeBytes(of: UInt8.max, toByteOffset: 3, as: UInt8.self) % else: