diff --git a/.gitignore b/.gitignore index 6de07761431fd..2c30c459ab9f9 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ docs/_build .vs # clangd +.cache .clangd #==============================================================================# diff --git a/CMakeLists.txt b/CMakeLists.txt index 966c7bdd9639e..31d450f959d8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,10 @@ include(CheckSymbolExists) # This is primarily to support building smaller or faster project files. # +option(SWIFT_APPEND_VC_REV + "Embed the version control system revision in Swift" + TRUE) + option(SWIFT_INCLUDE_TOOLS "Generate build targets for swift tools" TRUE) @@ -365,6 +369,11 @@ option(SWIFT_REPORT_STATISTICS "Create json files which contain internal compilation statistics" FALSE) +# FIXME(wasm) Reflection tests are temporalily disabled due to lack of linker features +option(SWIFTWASM_DISABLE_REFLECTION_TEST + "Disable building swift-reflection-test for WebAssembly build" + FALSE) + # # User-configurable experimental options. Do not use in production builds. # @@ -398,6 +407,10 @@ option(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING "Enable experimental Swift differentiable programming features" FALSE) +option(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY + "Enable experimental Swift concurrency model" + FALSE) + # # End of user-configurable options. # @@ -848,6 +861,7 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_SDK_OVERLAY) message(STATUS "") message(STATUS "Differentiable Programming Support: ${SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING}") + message(STATUS "Concurrency Support: ${SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY}") message(STATUS "") else() message(STATUS "Not building Swift standard library, SDK overlays, and runtime") @@ -920,13 +934,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") endif() find_package(Python2 COMPONENTS Interpreter REQUIRED) -find_package(Python3 COMPONENTS Interpreter) -if(NOT Python3_Interpreter_FOUND) - message(WARNING "Python3 not found, using python2 as a fallback") - add_executable(Python3::Interpreter IMPORTED) - set_target_properties(Python3::Interpreter PROPERTIES - IMPORTED_LOCATION ${Python2_EXECUTABLE}) -endif() +find_package(Python3 COMPONENTS Interpreter REQUIRED) # # Find optional dependencies. @@ -1137,6 +1145,15 @@ if(SWIFT_INCLUDE_TOOLS) # Refer to the large comment above the add_subdirectory(stdlib) call. # https://bugs.swift.org/browse/SR-5975 add_subdirectory(tools) + + # Localization targets are configured in a way that assume the swift + # frontend is being built, so trying to include them for other builds + # (like stdlib) fail! + # + # Diagnostics information is only useful for the frontend compiler + # anyway, so let's only include it if the compiler is being built, + # which at the moment seems like if SWIFT_INCLUDE_TOOLS is defined. + add_subdirectory(localization) endif() add_subdirectory(utils) diff --git a/README.md b/README.md index 838b5c96eaa39..e99f28b549c8d 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,10 @@ more links in the SwiftWasm ecosystem. | **OS** | **Architecture** | **Build** | |---|:---:|:---:| |**[Ubuntu 16.04 ](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/ppc64le_ubuntu_16_04.json)** | PPC64LE |[![Build Status](https://ci-external.swift.org/job/oss-swift-5.1-RA-linux-ubuntu-16.04-ppc64le/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-5.1-RA-linux-ubuntu-16.04-ppc64le)| -|**[Ubuntu 16.04 ](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_ubuntu_16.04.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-aarch64)| -|**[Ubuntu 18.04 ](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_ubuntu_18.04.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-aarch64)| +|**[Ubuntu 18.04](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_ubuntu_18.04_docker.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-ubuntu-18.04-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-ubuntu-18.04-aarch64)| +|**[Ubuntu 20.04](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_ubuntu_20.04_docker.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-ubuntu-20.04-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-ubuntu-20.04-aarch64)| +|**[CentOS 8 ](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_centos_8_docker.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-centos8-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-centos8-aarch64)| +|**[Amazon Linux 2](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/aarch64_amazon_linux_2_docker.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-amazon-linux-2-aarch64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-amazon-linux-2-aarch64)| |**[Android](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_16_04_LTS_android.json)** | ARMv7 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-android/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-android)| |**[Android](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_16_04_LTS_android.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-android-arm64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-16.04-android-arm64)| |**[Windows 2019 (VS 2017)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_windows_2019.json)** | x86_64 | [![Build Status](https://ci-external.swift.org/job/oss-swift-windows-x86_64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-windows-x86_64)| @@ -108,7 +110,7 @@ Please make sure you use Python 2.x. Python 3.x is not supported currently. #### macOS -To build for macOS, you need [Xcode 12 beta](https://developer.apple.com/xcode/resources/). +To build for macOS, you need [Xcode 12 beta 3](https://developer.apple.com/xcode/resources/). 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/cmake/modules/AddSwiftBenchmarkSuite.cmake b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake index ea36002f8eabe..49a28d34bc585 100644 --- a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake +++ b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake @@ -367,12 +367,17 @@ function (swift_benchmark_compile_archopts) "-F" "${sdk}/../../../Developer/Library/Frameworks" "-sdk" "${sdk}" "-no-link-objc-runtime") + + # If we are not compiling at -Onone and are performing WMO, always emit + # optimization-records. + if(NOT ${optflag} STREQUAL "Onone" AND "${bench_flags}" MATCHES "-whole-module.*") + list(APPEND common_options "-save-optimization-record=bitstream") + endif() endif() set(opt_view_main_dir) if(SWIFT_BENCHMARK_GENERATE_OPT_VIEW AND LLVM_HAVE_OPT_VIEWER_MODULES) if(NOT ${optflag} STREQUAL "Onone" AND "${bench_flags}" MATCHES "-whole-module.*") - list(APPEND common_options "-save-optimization-record") set(opt_view_main_dir "${objdir}/opt-view") endif() endif() @@ -737,11 +742,13 @@ function(swift_benchmark_compile) add_custom_target("check-${executable_target}" COMMAND "${swift-bin-dir}/Benchmark_Driver" "run" "-o" "O" "--output-dir" "${CMAKE_CURRENT_BINARY_DIR}/logs" + "--architecture" "${arch}" "--swift-repo" "${SWIFT_SOURCE_DIR}" "--independent-samples" "${SWIFT_BENCHMARK_NUM_O_ITERATIONS}" COMMAND "${swift-bin-dir}/Benchmark_Driver" "run" "-o" "Onone" "--output-dir" "${CMAKE_CURRENT_BINARY_DIR}/logs" "--swift-repo" "${SWIFT_SOURCE_DIR}" + "--architecture" "${arch}" "--independent-samples" "${SWIFT_BENCHMARK_NUM_ONONE_ITERATIONS}" COMMAND "${swift-bin-dir}/Benchmark_Driver" "compare" "--log-dir" "${CMAKE_CURRENT_BINARY_DIR}/logs" diff --git a/benchmark/scripts/Benchmark_Driver b/benchmark/scripts/Benchmark_Driver index 388e262cdae25..56582af86f0eb 100755 --- a/benchmark/scripts/Benchmark_Driver +++ b/benchmark/scripts/Benchmark_Driver @@ -919,6 +919,12 @@ def parse_args(args): help="optimization level to use: {O,Onone,Osize}, (default: O)", default="O", ) + shared_benchmarks_parser.add_argument( + "--architecture", + metavar="architecture", + help="current architecture (e.g., x86_64, arm64, etc)", + default=None, + ) run_parser = subparsers.add_parser( "run", diff --git a/benchmark/scripts/compare_perf_tests.py b/benchmark/scripts/compare_perf_tests.py index ef461dbde2aa6..57ad939720820 100755 --- a/benchmark/scripts/compare_perf_tests.py +++ b/benchmark/scripts/compare_perf_tests.py @@ -551,9 +551,9 @@ def compare(name): comparisons = list(map(compare, comparable_tests)) - def partition(l, p): + def partition(items, p): return functools.reduce( - lambda x, y: x[not p(y)].append(y) or x, l, ([], []) + lambda x, y: x[not p(y)].append(y) or x, items, ([], []) ) decreased, not_decreased = partition( diff --git a/benchmark/scripts/perf_test_driver/perf_test_driver.py b/benchmark/scripts/perf_test_driver/perf_test_driver.py index ef8ffd2b600cd..c662345333682 100644 --- a/benchmark/scripts/perf_test_driver/perf_test_driver.py +++ b/benchmark/scripts/perf_test_driver/perf_test_driver.py @@ -15,8 +15,10 @@ from __future__ import print_function import functools +import glob import multiprocessing import os +import platform import re import subprocess @@ -80,6 +82,22 @@ def _unwrap_self(args): return type(args[0]).process_input(*args) +def get_benchmark_executable(binary_dir, opt_level): + suffix = opt_level + "-" + platform.machine() + "*" + pattern = os.path.join(binary_dir, "Benchmark_" + suffix) + executables = glob.glob(pattern) + if len(executables) == 0: + raise ValueError( + "No benchmark executable for file name pattern " + + pattern + " found") + if len(executables) > 1: + raise ValueError( + "Multiple benchmark executables for file name pattern " + + pattern + " found\n" + + str(executables)) + return executables[0] + + BenchmarkDriver_OptLevels = ["Onone", "O", "Osize"] @@ -92,7 +110,7 @@ def __init__( opt_levels=BenchmarkDriver_OptLevels, ): self.targets = [ - (os.path.join(binary_dir, "Benchmark_%s" % o), o) for o in opt_levels + (get_benchmark_executable(binary_dir, o), o) for o in opt_levels ] self.xfail_list = xfail_list self.enable_parallel = enable_parallel @@ -112,8 +130,8 @@ def run_for_opt_level(self, binary, opt_level, test_filter): print("testing driver at path: %s" % binary) names = [] output = subprocess.check_output([binary, "--list"], universal_newlines=True) - for l in output.split("\n")[1:]: - m = BENCHMARK_OUTPUT_RE.match(l) + for line in output.split("\n")[1:]: + m = BENCHMARK_OUTPUT_RE.match(line) if m is None: continue names.append(m.group(1)) diff --git a/benchmark/single-source/CharacterProperties.swift b/benchmark/single-source/CharacterProperties.swift index 3a0872c907176..49488f0e8593d 100644 --- a/benchmark/single-source/CharacterProperties.swift +++ b/benchmark/single-source/CharacterProperties.swift @@ -53,104 +53,94 @@ extension Character { // Fetch the CharacterSet for every call -func isControl(_ c: Character) -> Bool { - return CharacterSet.controlCharacters.contains(c.firstScalar) -} func isAlphanumeric(_ c: Character) -> Bool { return CharacterSet.alphanumerics.contains(c.firstScalar) } -func isLowercase(_ c: Character) -> Bool { - return CharacterSet.lowercaseLetters.contains(c.firstScalar) +func isCapitalized(_ c: Character) -> Bool { + return CharacterSet.capitalizedLetters.contains(c.firstScalar) } -func isPunctuation(_ c: Character) -> Bool { - return CharacterSet.punctuationCharacters.contains(c.firstScalar) +func isControl(_ c: Character) -> Bool { + return CharacterSet.controlCharacters.contains(c.firstScalar) } -func isWhitespace(_ c: Character) -> Bool { - return CharacterSet.whitespaces.contains(c.firstScalar) +func isDecimal(_ c: Character) -> Bool { + return CharacterSet.decimalDigits.contains(c.firstScalar) } func isLetter(_ c: Character) -> Bool { return CharacterSet.letters.contains(c.firstScalar) } +func isLowercase(_ c: Character) -> Bool { + return CharacterSet.lowercaseLetters.contains(c.firstScalar) +} func isUppercase(_ c: Character) -> Bool { return CharacterSet.uppercaseLetters.contains(c.firstScalar) } -func isDecimal(_ c: Character) -> Bool { - return CharacterSet.decimalDigits.contains(c.firstScalar) -} func isNewline(_ c: Character) -> Bool { return CharacterSet.newlines.contains(c.firstScalar) } -func isCapitalized(_ c: Character) -> Bool { - return CharacterSet.capitalizedLetters.contains(c.firstScalar) +func isWhitespace(_ c: Character) -> Bool { + return CharacterSet.whitespaces.contains(c.firstScalar) +} +func isPunctuation(_ c: Character) -> Bool { + return CharacterSet.punctuationCharacters.contains(c.firstScalar) } // Stash the set -let controlCharacters = CharacterSet.controlCharacters -func isControlStashed(_ c: Character) -> Bool { - return controlCharacters.contains(c.firstScalar) -} let alphanumerics = CharacterSet.alphanumerics func isAlphanumericStashed(_ c: Character) -> Bool { return alphanumerics.contains(c.firstScalar) } -let lowercaseLetters = CharacterSet.lowercaseLetters -func isLowercaseStashed(_ c: Character) -> Bool { - return lowercaseLetters.contains(c.firstScalar) +let capitalizedLetters = CharacterSet.capitalizedLetters +func isCapitalizedStashed(_ c: Character) -> Bool { + return capitalizedLetters.contains(c.firstScalar) } -let punctuationCharacters = CharacterSet.punctuationCharacters -func isPunctuationStashed(_ c: Character) -> Bool { - return punctuationCharacters.contains(c.firstScalar) +let controlCharacters = CharacterSet.controlCharacters +func isControlStashed(_ c: Character) -> Bool { + return controlCharacters.contains(c.firstScalar) } -let whitespaces = CharacterSet.whitespaces -func isWhitespaceStashed(_ c: Character) -> Bool { - return whitespaces.contains(c.firstScalar) +let decimalDigits = CharacterSet.decimalDigits +func isDecimalStashed(_ c: Character) -> Bool { + return decimalDigits.contains(c.firstScalar) } let letters = CharacterSet.letters func isLetterStashed(_ c: Character) -> Bool { return letters.contains(c.firstScalar) } +let lowercaseLetters = CharacterSet.lowercaseLetters +func isLowercaseStashed(_ c: Character) -> Bool { + return lowercaseLetters.contains(c.firstScalar) +} let uppercaseLetters = CharacterSet.uppercaseLetters func isUppercaseStashed(_ c: Character) -> Bool { return uppercaseLetters.contains(c.firstScalar) } -let decimalDigits = CharacterSet.decimalDigits -func isDecimalStashed(_ c: Character) -> Bool { - return decimalDigits.contains(c.firstScalar) -} let newlines = CharacterSet.newlines func isNewlineStashed(_ c: Character) -> Bool { return newlines.contains(c.firstScalar) } -let capitalizedLetters = CharacterSet.capitalizedLetters -func isCapitalizedStashed(_ c: Character) -> Bool { - return capitalizedLetters.contains(c.firstScalar) +let whitespaces = CharacterSet.whitespaces +func isWhitespaceStashed(_ c: Character) -> Bool { + return whitespaces.contains(c.firstScalar) +} +let punctuationCharacters = CharacterSet.punctuationCharacters +func isPunctuationStashed(_ c: Character) -> Bool { + return punctuationCharacters.contains(c.firstScalar) } func setupStash() { blackHole(workload) - blackHole(controlCharacters) blackHole(alphanumerics) - blackHole(lowercaseLetters) - blackHole(punctuationCharacters) - blackHole(whitespaces) + blackHole(capitalizedLetters) + blackHole(controlCharacters) + blackHole(decimalDigits) blackHole(letters) + blackHole(lowercaseLetters) blackHole(uppercaseLetters) - blackHole(decimalDigits) blackHole(newlines) - blackHole(capitalizedLetters) + blackHole(whitespaces) + blackHole(punctuationCharacters) } // Memoize the stashed set -var controlCharactersMemo = Set() -func isControlStashedMemo(_ c: Character) -> Bool { - let scalar = c.firstScalar - if controlCharactersMemo.contains(scalar.value) { return true } - if controlCharacters.contains(scalar) { - controlCharactersMemo.insert(scalar.value) - return true - } - return false -} var alphanumericsMemo = Set() func isAlphanumericStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar @@ -161,32 +151,32 @@ func isAlphanumericStashedMemo(_ c: Character) -> Bool { } return false } -var lowercaseLettersMemo = Set() -func isLowercaseStashedMemo(_ c: Character) -> Bool { +var capitalizedLettersMemo = Set() +func isCapitalizedStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if lowercaseLettersMemo.contains(scalar.value) { return true } - if lowercaseLetters.contains(scalar) { - lowercaseLettersMemo.insert(scalar.value) + if capitalizedLettersMemo.contains(scalar.value) { return true } + if capitalizedLetters.contains(scalar) { + capitalizedLettersMemo.insert(scalar.value) return true } return false } -var punctuationCharactersMemo = Set() -func isPunctuationStashedMemo(_ c: Character) -> Bool { +var controlCharactersMemo = Set() +func isControlStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if punctuationCharactersMemo.contains(scalar.value) { return true } - if punctuationCharacters.contains(scalar) { - punctuationCharactersMemo.insert(scalar.value) + if controlCharactersMemo.contains(scalar.value) { return true } + if controlCharacters.contains(scalar) { + controlCharactersMemo.insert(scalar.value) return true } return false } -var whitespacesMemo = Set() -func isWhitespaceStashedMemo(_ c: Character) -> Bool { +var decimalDigitsMemo = Set() +func isDecimalStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if whitespacesMemo.contains(scalar.value) { return true } - if whitespaces.contains(scalar) { - whitespacesMemo.insert(scalar.value) + if decimalDigitsMemo.contains(scalar.value) { return true } + if decimalDigits.contains(scalar) { + decimalDigitsMemo.insert(scalar.value) return true } return false @@ -201,22 +191,22 @@ func isLetterStashedMemo(_ c: Character) -> Bool { } return false } -var uppercaseLettersMemo = Set() -func isUppercaseStashedMemo(_ c: Character) -> Bool { +var lowercaseLettersMemo = Set() +func isLowercaseStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if uppercaseLettersMemo.contains(scalar.value) { return true } - if uppercaseLetters.contains(scalar) { - uppercaseLettersMemo.insert(scalar.value) + if lowercaseLettersMemo.contains(scalar.value) { return true } + if lowercaseLetters.contains(scalar) { + lowercaseLettersMemo.insert(scalar.value) return true } return false } -var decimalDigitsMemo = Set() -func isDecimalStashedMemo(_ c: Character) -> Bool { +var uppercaseLettersMemo = Set() +func isUppercaseStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if decimalDigitsMemo.contains(scalar.value) { return true } - if decimalDigits.contains(scalar) { - decimalDigitsMemo.insert(scalar.value) + if uppercaseLettersMemo.contains(scalar.value) { return true } + if uppercaseLetters.contains(scalar) { + uppercaseLettersMemo.insert(scalar.value) return true } return false @@ -231,12 +221,22 @@ func isNewlineStashedMemo(_ c: Character) -> Bool { } return false } -var capitalizedLettersMemo = Set() -func isCapitalizedStashedMemo(_ c: Character) -> Bool { +var whitespacesMemo = Set() +func isWhitespaceStashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar - if capitalizedLettersMemo.contains(scalar.value) { return true } - if capitalizedLetters.contains(scalar) { - capitalizedLettersMemo.insert(scalar.value) + if whitespacesMemo.contains(scalar.value) { return true } + if whitespaces.contains(scalar) { + whitespacesMemo.insert(scalar.value) + return true + } + return false +} +var punctuationCharactersMemo = Set() +func isPunctuationStashedMemo(_ c: Character) -> Bool { + let scalar = c.firstScalar + if punctuationCharactersMemo.contains(scalar.value) { return true } + if punctuationCharacters.contains(scalar) { + punctuationCharactersMemo.insert(scalar.value) return true } return false @@ -244,16 +244,16 @@ func isCapitalizedStashedMemo(_ c: Character) -> Bool { func setupMemo() { blackHole(workload) - blackHole(controlCharactersMemo) blackHole(alphanumericsMemo) - blackHole(lowercaseLettersMemo) - blackHole(punctuationCharactersMemo) - blackHole(whitespacesMemo) + blackHole(capitalizedLettersMemo) + blackHole(controlCharactersMemo) + blackHole(decimalDigitsMemo) blackHole(lettersMemo) + blackHole(lowercaseLettersMemo) blackHole(uppercaseLettersMemo) - blackHole(decimalDigitsMemo) blackHole(newlinesMemo) - blackHole(capitalizedLettersMemo) + blackHole(whitespacesMemo) + blackHole(punctuationCharactersMemo) } // Precompute whole scalar set @@ -271,69 +271,69 @@ func precompute(_ charSet: CharacterSet, capacity: Int) -> Set { } return result } -var controlCharactersPrecomputed: Set = - precompute(controlCharacters, capacity: 24951) -func isControlPrecomputed(_ c: Character) -> Bool { - return controlCharactersPrecomputed.contains(c.firstScalar.value) -} var alphanumericsPrecomputed: Set = precompute(alphanumerics, capacity: 122647) func isAlphanumericPrecomputed(_ c: Character) -> Bool { return alphanumericsPrecomputed.contains(c.firstScalar.value) } -var lowercaseLettersPrecomputed: Set = - precompute(lowercaseLetters, capacity: 2063) -func isLowercasePrecomputed(_ c: Character) -> Bool { - return lowercaseLettersPrecomputed.contains(c.firstScalar.value) +var capitalizedLettersPrecomputed: Set = + precompute(capitalizedLetters, capacity: 31) +func isCapitalizedPrecomputed(_ c: Character) -> Bool { + return capitalizedLettersPrecomputed.contains(c.firstScalar.value) } -var punctuationCharactersPrecomputed: Set = - precompute(punctuationCharacters, capacity: 770) -func isPunctuationPrecomputed(_ c: Character) -> Bool { - return punctuationCharactersPrecomputed.contains(c.firstScalar.value) +var controlCharactersPrecomputed: Set = + precompute(controlCharacters, capacity: 24951) +func isControlPrecomputed(_ c: Character) -> Bool { + return controlCharactersPrecomputed.contains(c.firstScalar.value) } -var whitespacesPrecomputed: Set = - precompute(whitespaces, capacity: 19) -func isWhitespacePrecomputed(_ c: Character) -> Bool { - return whitespacesPrecomputed.contains(c.firstScalar.value) +var decimalDigitsPrecomputed: Set = + precompute(decimalDigits, capacity: 590) +func isDecimalPrecomputed(_ c: Character) -> Bool { + return decimalDigitsPrecomputed.contains(c.firstScalar.value) } var lettersPrecomputed: Set = precompute(letters, capacity: 121145) func isLetterPrecomputed(_ c: Character) -> Bool { return lettersPrecomputed.contains(c.firstScalar.value) } +var lowercaseLettersPrecomputed: Set = + precompute(lowercaseLetters, capacity: 2063) +func isLowercasePrecomputed(_ c: Character) -> Bool { + return lowercaseLettersPrecomputed.contains(c.firstScalar.value) +} var uppercaseLettersPrecomputed: Set = precompute(uppercaseLetters, capacity: 1733) func isUppercasePrecomputed(_ c: Character) -> Bool { return uppercaseLettersPrecomputed.contains(c.firstScalar.value) } -var decimalDigitsPrecomputed: Set = - precompute(decimalDigits, capacity: 590) -func isDecimalPrecomputed(_ c: Character) -> Bool { - return decimalDigitsPrecomputed.contains(c.firstScalar.value) -} var newlinesPrecomputed: Set = precompute(newlines, capacity: 7) func isNewlinePrecomputed(_ c: Character) -> Bool { return newlinesPrecomputed.contains(c.firstScalar.value) } -var capitalizedLettersPrecomputed: Set = - precompute(capitalizedLetters, capacity: 31) -func isCapitalizedPrecomputed(_ c: Character) -> Bool { - return capitalizedLettersPrecomputed.contains(c.firstScalar.value) +var whitespacesPrecomputed: Set = + precompute(whitespaces, capacity: 19) +func isWhitespacePrecomputed(_ c: Character) -> Bool { + return whitespacesPrecomputed.contains(c.firstScalar.value) +} +var punctuationCharactersPrecomputed: Set = + precompute(punctuationCharacters, capacity: 770) +func isPunctuationPrecomputed(_ c: Character) -> Bool { + return punctuationCharactersPrecomputed.contains(c.firstScalar.value) } func setupPrecomputed() { blackHole(workload) - blackHole(controlCharactersPrecomputed) blackHole(alphanumericsPrecomputed) - blackHole(lowercaseLettersPrecomputed) - blackHole(punctuationCharactersPrecomputed) - blackHole(whitespacesPrecomputed) + blackHole(capitalizedLettersPrecomputed) + blackHole(controlCharactersPrecomputed) + blackHole(decimalDigitsPrecomputed) blackHole(lettersPrecomputed) + blackHole(lowercaseLettersPrecomputed) blackHole(uppercaseLettersPrecomputed) - blackHole(decimalDigitsPrecomputed) blackHole(newlinesPrecomputed) - blackHole(capitalizedLettersPrecomputed) + blackHole(whitespacesPrecomputed) + blackHole(punctuationCharactersPrecomputed) } // Compute on the fly @@ -361,16 +361,16 @@ let workload = """ public func run_CharacterPropertiesFetch(_ N: Int) { for _ in 1...N { for c in workload { - blackHole(isControl(c)) blackHole(isAlphanumeric(c)) - blackHole(isLowercase(c)) - blackHole(isPunctuation(c)) - blackHole(isWhitespace(c)) + blackHole(isCapitalized(c)) + blackHole(isControl(c)) + blackHole(isDecimal(c)) blackHole(isLetter(c)) + blackHole(isLowercase(c)) blackHole(isUppercase(c)) - blackHole(isDecimal(c)) blackHole(isNewline(c)) - blackHole(isCapitalized(c)) + blackHole(isWhitespace(c)) + blackHole(isPunctuation(c)) } } } @@ -379,16 +379,16 @@ public func run_CharacterPropertiesFetch(_ N: Int) { public func run_CharacterPropertiesStashed(_ N: Int) { for _ in 1...N { for c in workload { - blackHole(isControlStashed(c)) blackHole(isAlphanumericStashed(c)) - blackHole(isLowercaseStashed(c)) - blackHole(isPunctuationStashed(c)) - blackHole(isWhitespaceStashed(c)) + blackHole(isCapitalizedStashed(c)) + blackHole(isControlStashed(c)) + blackHole(isDecimalStashed(c)) blackHole(isLetterStashed(c)) + blackHole(isLowercaseStashed(c)) blackHole(isUppercaseStashed(c)) - blackHole(isDecimalStashed(c)) blackHole(isNewlineStashed(c)) - blackHole(isCapitalizedStashed(c)) + blackHole(isWhitespaceStashed(c)) + blackHole(isPunctuationStashed(c)) } } } @@ -397,16 +397,16 @@ public func run_CharacterPropertiesStashed(_ N: Int) { public func run_CharacterPropertiesStashedMemo(_ N: Int) { for _ in 1...N { for c in workload { - blackHole(isControlStashedMemo(c)) blackHole(isAlphanumericStashedMemo(c)) - blackHole(isLowercaseStashedMemo(c)) - blackHole(isPunctuationStashedMemo(c)) - blackHole(isWhitespaceStashedMemo(c)) + blackHole(isCapitalizedStashedMemo(c)) + blackHole(isControlStashedMemo(c)) + blackHole(isDecimalStashedMemo(c)) blackHole(isLetterStashedMemo(c)) + blackHole(isLowercaseStashedMemo(c)) blackHole(isUppercaseStashedMemo(c)) - blackHole(isDecimalStashedMemo(c)) blackHole(isNewlineStashedMemo(c)) - blackHole(isCapitalizedStashedMemo(c)) + blackHole(isWhitespaceStashedMemo(c)) + blackHole(isPunctuationStashedMemo(c)) } } } @@ -415,16 +415,16 @@ public func run_CharacterPropertiesStashedMemo(_ N: Int) { public func run_CharacterPropertiesPrecomputed(_ N: Int) { for _ in 1...N { for c in workload { - blackHole(isControlPrecomputed(c)) blackHole(isAlphanumericPrecomputed(c)) - blackHole(isLowercasePrecomputed(c)) - blackHole(isPunctuationPrecomputed(c)) - blackHole(isWhitespacePrecomputed(c)) + blackHole(isCapitalizedPrecomputed(c)) + blackHole(isControlPrecomputed(c)) + blackHole(isDecimalPrecomputed(c)) blackHole(isLetterPrecomputed(c)) + blackHole(isLowercasePrecomputed(c)) blackHole(isUppercasePrecomputed(c)) - blackHole(isDecimalPrecomputed(c)) blackHole(isNewlinePrecomputed(c)) - blackHole(isCapitalizedPrecomputed(c)) + blackHole(isWhitespacePrecomputed(c)) + blackHole(isPunctuationPrecomputed(c)) } } } diff --git a/benchmark/single-source/CharacterProperties.swift.gyb b/benchmark/single-source/CharacterProperties.swift.gyb index f6c47b1874db5..604b211c1e8b7 100644 --- a/benchmark/single-source/CharacterProperties.swift.gyb +++ b/benchmark/single-source/CharacterProperties.swift.gyb @@ -52,27 +52,27 @@ extension Character { var firstScalar: UnicodeScalar { return unicodeScalars.first! } } -% Properties = { "Alphanumeric": "alphanumerics", \ -% "Capitalized": "capitalizedLetters", \ -% "Control": "controlCharacters", \ -% "Decimal": "decimalDigits", \ -% "Letter": "letters", \ -% "Lowercase": "lowercaseLetters", \ -% "Uppercase": "uppercaseLetters", \ -% "Newline": "newlines", \ -% "Whitespace": "whitespaces", \ -% "Punctuation": "punctuationCharacters" \ -% } +% Properties = [ ( "Alphanumeric", "alphanumerics"), +% ( "Capitalized", "capitalizedLetters"), +% ( "Control", "controlCharacters"), +% ( "Decimal", "decimalDigits"), +% ( "Letter", "letters"), +% ( "Lowercase", "lowercaseLetters"), +% ( "Uppercase", "uppercaseLetters"), +% ( "Newline", "newlines"), +% ( "Whitespace", "whitespaces"), +% ( "Punctuation", "punctuationCharacters") +% ] // Fetch the CharacterSet for every call -% for Property, Set in Properties.items(): +% for Property, Set in Properties: func is${Property}(_ c: Character) -> Bool { return CharacterSet.${Set}.contains(c.firstScalar) } % end // Stash the set -% for Property, Set in Properties.items(): +% for Property, Set in Properties: let ${Set} = CharacterSet.${Set} func is${Property}Stashed(_ c: Character) -> Bool { return ${Set}.contains(c.firstScalar) @@ -81,13 +81,13 @@ func is${Property}Stashed(_ c: Character) -> Bool { func setupStash() { blackHole(workload) -% for Property, Set in Properties.items(): +% for Property, Set in Properties: blackHole(${Set}) % end } // Memoize the stashed set -% for Property, Set in Properties.items(): +% for Property, Set in Properties: var ${Set}Memo = Set() func is${Property}StashedMemo(_ c: Character) -> Bool { let scalar = c.firstScalar @@ -102,7 +102,7 @@ func is${Property}StashedMemo(_ c: Character) -> Bool { func setupMemo() { blackHole(workload) -% for Property, Set in Properties.items(): +% for Property, Set in Properties: blackHole(${Set}Memo) % end } @@ -130,7 +130,7 @@ func precompute(_ charSet: CharacterSet, capacity: Int) -> Set { capitalizedLetters=31 ) }% -% for Property, Set in Properties.items(): +% for Property, Set in Properties: var ${Set}Precomputed: Set = precompute(${Set}, capacity: ${precomputed_capacity[Set]}) func is${Property}Precomputed(_ c: Character) -> Bool { @@ -140,7 +140,7 @@ func is${Property}Precomputed(_ c: Character) -> Bool { func setupPrecomputed() { blackHole(workload) -% for Property, Set in Properties.items(): +% for Property, Set in Properties: blackHole(${Set}Precomputed) %#// print("${Set}: \(${Set}Precomputed.count)") % end @@ -171,7 +171,7 @@ let workload = """ public func run_CharacterPropertiesFetch(_ N: Int) { for _ in 1...N { for c in workload { - % for Property, Set in Properties.items(): + % for Property, Set in Properties: blackHole(is${Property}(c)) % end } @@ -182,7 +182,7 @@ public func run_CharacterPropertiesFetch(_ N: Int) { public func run_CharacterPropertiesStashed(_ N: Int) { for _ in 1...N { for c in workload { - % for Property, Set in Properties.items(): + % for Property, Set in Properties: blackHole(is${Property}Stashed(c)) % end } @@ -193,7 +193,7 @@ public func run_CharacterPropertiesStashed(_ N: Int) { public func run_CharacterPropertiesStashedMemo(_ N: Int) { for _ in 1...N { for c in workload { - % for Property, Set in Properties.items(): + % for Property, Set in Properties: blackHole(is${Property}StashedMemo(c)) % end } @@ -204,7 +204,7 @@ public func run_CharacterPropertiesStashedMemo(_ N: Int) { public func run_CharacterPropertiesPrecomputed(_ N: Int) { for _ in 1...N { for c in workload { - % for Property, Set in Properties.items(): + % for Property, Set in Properties: blackHole(is${Property}Precomputed(c)) % end } diff --git a/benchmark/single-source/DropFirst.swift.gyb b/benchmark/single-source/DropFirst.swift.gyb index 7b52ee88b878f..2b5b3cf50b364 100644 --- a/benchmark/single-source/DropFirst.swift.gyb +++ b/benchmark/single-source/DropFirst.swift.gyb @@ -38,9 +38,9 @@ Sequences = [ ('AnyCollection', 'AnyCollection(0..= 5.4 + type ::= 'Qu' // opaque result type (of current decl) + // used for ObjC class runtime name purposes. + #endif + Opaque return types have a special short representation in the mangling of their defining entity. In structural position, opaque types are fully qualified by mangling the defining entity for the opaque declaration and the substitutions diff --git a/docs/CompilerPerformance.md b/docs/CompilerPerformance.md index 1c5a4546cdb63..7a11bfb85df41 100644 --- a/docs/CompilerPerformance.md +++ b/docs/CompilerPerformance.md @@ -559,30 +559,6 @@ compilers on hand while you're working. 0.0001 (100.0%) 0.0001 (100.0%) 0.0001 (100.0%) 0.0490 (100.0%) Total ``` - - `-Xfrontend -debug-time-compilation`: asks each frontend to print out timers - for each phase of its execution. Its output (per-frontend) looks like this: - - ``` - ===-------------------------------------------------------------------------=== - Swift compilation - ===-------------------------------------------------------------------------=== - Total Execution Time: 0.0876 seconds (0.0877 wall clock) - - ---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- - 0.0241 ( 53.9%) 0.0394 ( 92.0%) 0.0635 ( 72.5%) 0.0635 ( 72.5%) Import resolution - 0.0170 ( 38.0%) 0.0025 ( 5.8%) 0.0195 ( 22.3%) 0.0195 ( 22.2%) Type checking / Semantic analysis - 0.0013 ( 3.0%) 0.0004 ( 0.8%) 0.0017 ( 1.9%) 0.0017 ( 1.9%) LLVM output - 0.0010 ( 2.3%) 0.0003 ( 0.7%) 0.0013 ( 1.5%) 0.0013 ( 1.5%) SILGen - 0.0006 ( 1.4%) 0.0002 ( 0.4%) 0.0008 ( 0.9%) 0.0008 ( 0.9%) IRGen - 0.0004 ( 0.8%) 0.0000 ( 0.1%) 0.0004 ( 0.5%) 0.0004 ( 0.5%) SIL optimization - 0.0002 ( 0.5%) 0.0001 ( 0.1%) 0.0003 ( 0.3%) 0.0003 ( 0.3%) LLVM optimization - 0.0001 ( 0.1%) 0.0000 ( 0.1%) 0.0001 ( 0.1%) 0.0001 ( 0.1%) Parsing - 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) SIL verification (pre-optimization) - 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) SIL verification (post-optimization) - 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) AST verification - 0.0448 (100.0%) 0.0428 (100.0%) 0.0876 (100.0%) 0.0877 (100.0%) Total - ``` - - `-Xfrontend -debug-time-function-bodies`: asks each frontend to print out the time spent typechecking _every function_ in the program, sorted by time taken. The output is therefore voluminous, but can help when reducing a @@ -1053,10 +1029,8 @@ getting slower between versions: parameter of the script). Reconfirm that _just those two isolated frontend processes_ still show the regression you're interested in isolating. - 6. Check high-level diagnostic output between the two compilers, either the - newer unified stats reporter (`-stats-output-dir`) or the older flags - (`-Xfrontend -debug-time-compilation` and friends). Comparing the two will - often guide the search. + 6. Check the value of performance counters between the two compilers via the + unified stats reporter (`-stats-output-dir`). 7. Run both frontend processes under a profiler and compare the profiles in detail. At this point there ought to be _some_ sign of a difference, either diff --git a/docs/ContinuousIntegration.md b/docs/ContinuousIntegration.md index c6c381d7d4d7a..545b08a5e5006 100644 --- a/docs/ContinuousIntegration.md +++ b/docs/ContinuousIntegration.md @@ -92,9 +92,11 @@ macOS platform | @swift-ci Please smoke benchmark | S Linux platform | @swift-ci Please test Linux platform | Swift Test Linux Platform (smoke test)
Swift Test Linux Platform Linux platform | @swift-ci Please clean test Linux platform | Swift Test Linux Platform (smoke test)
Swift Test Linux Platform macOS platform | @swift-ci Please ASAN test | Swift ASAN Test OS X Platform -Ubuntu 20.04 | @swift-ci Please test Ubuntu 20.04 platform | Swift Test Ubuntu 20.04 Platform -CentOS 8 | @swift-ci Please test CentOS 8 platform | Swift Test CentOS 8 Platform -Amazon Linux 2 | @swift-ci Please test Amazon Linux 2 platform | Swift Test Amazon Linux 2 Platform +Ubuntu 18.04 | @swift-ci Please test Ubuntu 18.04 platform | Swift Test Ubuntu 18.04 Platform +Ubuntu 20.04 | @swift-ci Please test Ubuntu 20.04 platform | Swift Test Ubuntu 20.04 Platform +CentOS 7 | @swift-ci Please test CentOS 7 platform | Swift Test CentOS 7 Platform +CentOS 8 | @swift-ci Please test CentOS 8 platform | Swift Test CentOS 8 Platform +Amazon Linux 2 | @swift-ci Please test Amazon Linux 2 platform | Swift Test Amazon Linux 2 Platform The core principles of validation testing is that: diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index 91b396adada17..0e1196f138cca 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -1192,7 +1192,14 @@ extension Optional: Differentiable where Wrapped: Differentiable { @noDerivative public var zeroTangentVectorInitializer: () -> TangentVector { - { TangentVector(.zero) } + switch self { + case nil: + return { TangentVector(nil) } + case let x?: + return { [zeroTanInit = x.zeroTangentVectorInitializer] in + TangentVector(zeroTanInit()) + } + } } } ``` diff --git a/docs/README.md b/docs/README.md index 1492d1ef48430..cc4c4815bc9a9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -105,7 +105,7 @@ documentation, please create a thread on the Swift forums under the - [RequestEvaluator.md](/docs/RequestEvaluator.md): Describes the request evaluator architecture, which is used for lazy type-checking and efficient caching. - - [Literal.md](/docs/Literal.md): + - [Literals.md](/docs/Literals.md): Describes type-checking and inference specifically for literals. - [Serialization.rst](/docs/Serialization.rst): Gives an overview of the LLVM bitcode format used for swiftmodules. diff --git a/docs/SIL.rst b/docs/SIL.rst index ab25e2cce843c..f1a5e7a8c82e0 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -962,13 +962,13 @@ called within the addressor. The function is a getter of a lazy property for which the backing storage is an ``Optional`` of the property's type. The getter contains a top-level -``switch_enum`` (or ``switch_enum_addr``), which tests if the lazy property +`switch_enum`_ (or `switch_enum_addr`_), which tests if the lazy property is already computed. In the ``None``-case, the property is computed and stored to the backing storage of the property. After the first call of a lazy property getter, it is guaranteed that the property is computed and consecutive calls always execute the ``Some``-case of -the top-level ``switch_enum``. +the top-level `switch_enum`_. :: sil-function-attribute ::= '[weak_imported]' @@ -1035,7 +1035,10 @@ Basic Blocks sil-basic-block ::= sil-label sil-instruction-def* sil-terminator sil-label ::= sil-identifier ('(' sil-argument (',' sil-argument)* ')')? ':' - sil-argument ::= sil-value-name ':' sil-type + sil-value-ownership-kind ::= @owned + sil-value-ownership-kind ::= @guaranteed + sil-value-ownership-kind ::= @unowned + sil-argument ::= sil-value-name ':' sil-value-ownership-kind? sil-type sil-instruction-result ::= sil-value-name sil-instruction-result ::= '(' (sil-value-name (',' sil-value-name)*)? ')' @@ -1067,12 +1070,12 @@ block:: Arguments to the entry point basic block, which has no predecessor, are bound by the function's caller:: - sil @foo : $(Int) -> Int { + sil @foo : $@convention(thin) (Int) -> Int { bb0(%x : $Int): return %x : $Int } - sil @bar : $(Int, Int) -> () { + sil @bar : $@convention(thin) (Int, Int) -> () { bb0(%x : $Int, %y : $Int): %foo = function_ref @foo %1 = apply %foo(%x) : $(Int) -> Int @@ -1081,6 +1084,17 @@ are bound by the function's caller:: return %3 : $() } +When a function is in Ownership SSA, arguments additionally have an explicit +annotated convention that describe the ownership semantics of the argument +value:: + + sil [ossa] @baz : $@convention(thin) (Int, @owned String, @guaranteed String, @unowned String) -> () { + bb0(%x : $Int, %y : @owned $String, %z : @guaranteed $String, %w : @unowned $String): + ... + } + +Note that the first argument (``%x``) has an implicit ownership kind of +``@none`` since all trivial values have ``@none`` ownership. Debug Information ~~~~~~~~~~~~~~~~~ @@ -1579,6 +1593,249 @@ of functions returning uninhabited types. An ``unreachable`` instruction that survives guaranteed DCE and is not immediately preceded by a no-return application is a dataflow error. +Ownership SSA +------------- + +A SILFunction marked with the ``[ossa]`` function attribute is considered to be +in Ownership SSA form. Ownership SSA is an augmented version of SSA that +enforces ownership invariants by imbuing value-operand edges with semantic +ownership information. All SIL values are statically assigned an ownership kind +that defines the ownership semantics that the value models. All SIL operands +that use a SIL value are required to be able to be semantically partitioned in +between "normal uses" that just require the value to be live and "consuming +uses" that end the lifetime of the value and after which the value can no longer +be used. Since operands that are consuming uses end a value's lifetime, +naturally we must have that the consuming use points jointly post-dominate all +non-consuming use points and that a value must be consumed exactly once along +all reachable program paths, preventing leaks and use-after-frees. As an +example, consider the following SIL example with partitioned defs/uses annotated +inline:: + + sil @stash_and_cast : $@convention(thin) (@owned Klass) -> @owned SuperKlass { + bb0(%kls1 : @owned $Klass): // Definition of %kls1 + + // "Normal Use" kls1. + // Definition of %kls2. + %kls2 = copy_value %kls1 : $Klass + + // "Consuming Use" of %kls2 to store it into a global. Stores in ossa are + // consuming since memory is generally assumed to have "owned" + // semantics. After this instruction executes, we can no longer use %kls2 + // without triggering an ownership violation. + store %kls2 to [init] %globalMem : $*Klass + + // "Consuming Use" of %kls1. + // Definition of %kls1Casted. + %kls1Casted = upcast %kls1 : $Klass to $SuperKlass + + // "Consuming Use" of %kls1Casted + return %kls1Casted : $SuperKlass + } + +Notice how every value in the SIL above has a partionable set of uses with +normal uses always before consuming uses. Any such violations of ownership +semantics would trigger a static SILVerifier error allowing us to know that we +do not have any leaks or use-after-frees in the above code. + +The semantics in the previous example is of just one form of ownership semantics +supported: "owned" semantics. In SIL, we allow for values to have one of four +different ownership kinds: + +* **None**. This is used to represent values that do not require memory + management and are outside of Ownership SSA invariants. Examples: trivial + values (e.x.: Int, Float), non-payloaded cases of non-trivial enums (e.x.: + Optional.none), all address types. + +* **Owned**. A value that exists independently of any other value and is + consumed exactly once along all paths through a function by either a + destroy_value (actually destroying the value) or by a consuming instruction + that rebinds the value in some manner (e.x.: apply, casts, store). + +* **Guaranteed**. A value with a scoped lifetime whose liveness is dependent on + the lifetime of some other "base" owned or guaranteed value. Consumed by + instructions like `end_borrow`_. The "base" value is statically guaranteed to + be live at all of the value's paired end_borrow instructions. + +* **Unowned**. A value that is only guaranteed to be instantaneously valid and + must be copied before the value is used in an ``@owned`` or ``@guaranteed`` + context. This is needed both to model argument values with the ObjC unsafe + unowned argument convention and also to model the ownership resulting from + bitcasting a trivial type to a non-trivial type. This value should never be + consumed. + +We describe each of these semantics in more detail below. + +Value Ownership Kind +~~~~~~~~~~~~~~~~~~~~ + +Owned +````` + +Owned ownership models "move only" values. We require that each such value is +consumed exactly once along all program paths. The IR verifier will flag values +that are not consumed along a path as a leak and any double consumes as +use-after-frees. We model move operations via "forwarding uses" such as casts +and transforming terminators (e.x.: `switch_enum`_, `checked_cast_br`_) that +transform the input value, consuming it in the process, and producing a new +transformed owned value as a result. + +Putting this all together, one can view each owned SIL value as being +effectively a "move only value" except when explicitly copied by a +copy_value. This of course implies that ARC operations can be assumed to only +semantically effect the specific value that they are applied to /and/ that each +ARC constraint is able to be verified independently for each owned SILValue +derived from the ARC object. As an example, consider the following Swift/SIL:: + + // testcase.swift. + func doSomething(x : Klass) -> OtherKlass? { + return x as? OtherKlass + } + + // testcase.sil. A possible SILGen lowering + sil [ossa] @doSomething : $@convention(thin) (@guaranteed Klass) -> () { + bb0(%0 : @guaranteed Klass): + // Definition of '%1' + %1 = copy_value %0 : $Klass + + // Consume '%1'. This means '%1' can no longer be used after this point. We + // rebind '%1' in the destination blocks (bbYes, bbNo). + checked_cast_br %1 : $Klass to $OtherKlass, bbYes, bbNo + + bbYes(%2 : @owned $OtherKlass): // On success, the checked_cast_br forwards + // '%1' into '%2' after casting to OtherKlass. + + // Forward '%2' into '%3'. '%2' can not be used past this point in the + // function. + %3 = enum $Optional, case #Optional.some!enumelt, %2 : $OtherKlass + + // Forward '%3' into the branch. '%3' can not be used past this point. + br bbEpilog(%3 : $Optional) + + bbNo(%3 : @owned $Klass): // On failure, since we consumed '%1' already, we + // return the original '%1' as a new value '%3' + // so we can use it below. + // Actually destroy the underlying copy (``%1``) created by the copy_value + // in bb0. + destroy_value %3 : $Klass + + // We want to return nil here. So we create a new non-payloaded enum and + // pass it off to bbEpilog. + %4 = enum $Optional, case #Optional.none!enumelt + br bbEpilog(%4 : $Optional) + + bbEpilog(%5 : @owned $Optional): + // Consumes '%5' to return to caller. + return %5 : $Optional + } + +Notice how our individual copy (``%1``) threads its way through the IR using +forwarding of ``@owned`` ownership. These forwarding operations partition the +lifetime of the result of the copy_value into a set of disjoint individual owned +lifetimes (``%2``, ``%3``, ``%5``). + +Guaranteed +`````````` + +Guaranteed ownership models values that have a scoped dependent lifetime on a +"base value" with owned or guaranteed ownership. Due to this lifetime +dependence, the base value is required to be statically live over the entire +scope where the guaranteed value is valid. + +These explicit scopes are introduced into SIL by begin scope instructions (e.x.: +`begin_borrow`_, `load_borrow`_) that are paired with sets of jointly +post-dominating scope ending instructions (e.x.: `end_borrow`_):: + + sil [ossa] @guaranteed_values : $@convention(thin) (@owned Klass) -> () { + bb0(%0 : @owned $Klass): + %1 = begin_borrow %0 : $Klass + cond_br ..., bb1, bb2 + + bb1: + ... + end_borrow %1 : $Klass + destroy_value %0 : $Klass + br bb3 + + bb2: + ... + end_borrow %1 : $Klass + destroy_value %0 : $Klass + br bb3 + + bb3: + ... + } + +Notice how the `end_borrow`_ allow for a SIL generator to communicate to +optimizations that they can never shrink the lifetime of ``%0`` by moving +`destroy_value`_ above ``%1``. + +Values with guaranteed ownership follow a dataflow rule that states that +non-consuming "forwarding" uses of the guaranteed value are also guaranteed and +are recursively validated as being in the original values scope. This was a +choice we made to reduce idempotent scopes in the IR:: + + sil [ossa] @get_first_elt : $@convention(thin) (@guaranteed (String, String)) -> @owned String { + bb0(%0 : @guaranteed $(String, String)): + // %1 is validated as if it was apart of %0 and does not need its own begin_borrow/end_borrow. + %1 = tuple_extract %0 : $(String, String) + // So this copy_value is treated as a use of %0. + %2 = copy_value %1 : $String + return %2 : $String + } + +None +```` + +Values with None ownership are inert values that exist outside of the guarantees +of Ownership SSA. Some examples of such values are: + +* Trivially typed values such as: Int, Float, Double +* Non-payloaded non-trivial enums. +* Address types. + +Since values with none ownership exist outside of ownership SSA, they can be +used like normal SSA without violating ownership SSA invariants. This does not +mean that code does not potentially violate other SIL rules (consider memory +lifetime invariants):: + + sil @none_values : $@convention(thin) (Int, @in Klass) -> Int { + bb0(%0 : $Int, %1 : $*Klass): + + // %0, %1 are normal SSA values that can be used anywhere in the function + // without breaking Ownership SSA invariants. It could violate other + // invariants if for instance, we load from %1 after we destroy the object + // there. + destroy_addr %1 : $*Klass + + // If uncommented, this would violate memory lifetime invariants due to + // the ``destroy_addr %1`` above. But this would not violate the rules of + // Ownership SSA since addresses exist outside of the guarantees of + // Ownership SSA. + // + // %2 = load [take] %1 : $*Klass + + // I can return this object without worrying about needing to copy since + // none objects can be arbitrarily returned. + return %0 : $Int + } + +Unowned +``````` + +This is a form of ownership that is used to model two different use cases: + +* Arguments of functions with ObjC convention. This convention requires the + callee to copy the value before using it (preferably before any other code + runs). We do not model this flow sensitive property in SIL today, but we do + not allow for unowned values to be passed as owned or guaranteed values + without copying it first. + +* Values that are a conversion from a trivial value with None ownership to a + non-trivial value. As an example of this consider an unsafe bit cast of a + trivial pointer to a class. In that case, since we have no reason to assume + that the object will remain alive, we need to make a copy of the value. + Runtime Failure --------------- @@ -2592,6 +2849,11 @@ by an ``end_borrow`` instruction. All ``load_borrow`` instructions must be paired with exactly one ``end_borrow`` instruction along any path through the program. Until ``end_borrow``, it is illegal to invalidate or store to ``%0``. +begin_borrow +```````````` + +TODO + end_borrow `````````` @@ -4601,7 +4863,7 @@ but turns the control flow dependency into a data flow dependency. For address-only enums, `select_enum_addr`_ offers the same functionality for an indirectly referenced enum value in memory. -Like `switch_enum`_, ``select_enum`` must have a ``default`` case unless the +Like `switch_enum`_, `select_enum`_ must have a ``default`` case unless the enum can be exhaustively switched in the current function. select_enum_addr @@ -4626,7 +4888,7 @@ Selects one of the "case" or "default" operands based on the case of the referenced enum value. This is the address-only counterpart to `select_enum`_. -Like `switch_enum_addr`_, ``select_enum_addr`` must have a ``default`` case +Like `switch_enum_addr`_, `select_enum_addr`_ must have a ``default`` case unless the enum can be exhaustively switched in the current function. Protocol and Protocol Composition Types @@ -4650,10 +4912,10 @@ container may use one of several representations: type are class protocols, then the existential container for that type is address-only and referred to in the implementation as an *opaque existential container*. The value semantics of the existential container propagate to the - contained concrete value. Applying ``copy_addr`` to an opaque existential + contained concrete value. Applying `copy_addr`_ to an opaque existential container copies the contained concrete value, deallocating or reallocating the destination container's owned buffer if necessary. Applying - ``destroy_addr`` to an opaque existential container destroys the concrete + `destroy_addr`_ to an opaque existential container destroys the concrete value and deallocates any buffers owned by the existential container. The following instructions manipulate opaque existential containers: @@ -4705,8 +4967,8 @@ Some existential types may additionally support specialized representations when they contain certain known concrete types. For example, when Objective-C interop is available, the ``Error`` protocol existential supports a class existential container representation for ``NSError`` objects, so it -can be initialized from one using ``init_existential_ref`` instead of the -more expensive ``alloc_existential_box``:: +can be initialized from one using `init_existential_ref`_ instead of the +more expensive `alloc_existential_box`_:: bb(%nserror: $NSError): // The slow general way to form an Error, allocating a box and @@ -4739,9 +5001,9 @@ instruction is an address referencing the storage for the contained value, which remains uninitialized. The contained value must be ``store``-d or ``copy_addr``-ed to in order for the existential value to be fully initialized. If the existential container needs to be destroyed while the contained value -is uninitialized, ``deinit_existential_addr`` must be used to do so. A fully -initialized existential container can be destroyed with ``destroy_addr`` as -usual. It is undefined behavior to ``destroy_addr`` a partially-initialized +is uninitialized, `deinit_existential_addr`_ must be used to do so. A fully +initialized existential container can be destroyed with `destroy_addr`_ as +usual. It is undefined behavior to `destroy_addr`_ a partially-initialized existential container. init_existential_value @@ -4770,10 +5032,10 @@ deinit_existential_addr // composition type P Undoes the partial initialization performed by -``init_existential_addr``. ``deinit_existential_addr`` is only valid for +`init_existential_addr`_. `deinit_existential_addr`_ is only valid for existential containers that have been partially initialized by -``init_existential_addr`` but haven't had their contained value initialized. -A fully initialized existential must be destroyed with ``destroy_addr``. +`init_existential_addr`_ but haven't had their contained value initialized. +A fully initialized existential must be destroyed with `destroy_addr`_. deinit_existential_value ```````````````````````` @@ -4786,10 +5048,10 @@ deinit_existential_value // composition type P Undoes the partial initialization performed by -``init_existential_value``. ``deinit_existential_value`` is only valid for +`init_existential_value`_. `deinit_existential_value`_ is only valid for existential containers that have been partially initialized by -``init_existential_value`` but haven't had their contained value initialized. -A fully initialized existential must be destroyed with ``destroy_value``. +`init_existential_value`_ but haven't had their contained value initialized. +A fully initialized existential must be destroyed with `destroy_value`_. open_existential_addr ````````````````````` @@ -4866,7 +5128,7 @@ Extracts the class instance reference from a class existential container. The protocol conformances associated with this existential container are associated directly with the archetype ``@opened P``. This pointer can be used with any operation on archetypes, such as -``witness_method``. When the operand is of metatype type, the result +`witness_method`_. When the operand is of metatype type, the result will be the metatype of the opened archetype. init_existential_metatype @@ -4916,9 +5178,9 @@ alloc_existential_box Allocates a boxed existential container of type ``$P`` with space to hold a value of type ``$T'``. The box is not fully initialized until a valid value has been stored into the box. If the box must be deallocated before it is -fully initialized, ``dealloc_existential_box`` must be used. A fully +fully initialized, `dealloc_existential_box`_ must be used. A fully initialized box can be ``retain``-ed and ``release``-d like any -reference-counted type. The ``project_existential_box`` instruction is used +reference-counted type. The `project_existential_box`_ instruction is used to retrieve the address of the value inside the container. project_existential_box @@ -4936,7 +5198,7 @@ project_existential_box Projects the address of the value inside a boxed existential container. The address is dependent on the lifetime of the owner reference ``%0``. It is undefined behavior if the concrete type ``$T`` is not the same type for -which the box was allocated with ``alloc_existential_box``. +which the box was allocated with `alloc_existential_box`_. open_existential_box ```````````````````` @@ -4987,7 +5249,7 @@ Deallocates a boxed existential container. The value inside the existential buffer is not destroyed; either the box must be uninitialized, or the value must have been projected out and destroyed beforehand. It is undefined behavior if the concrete type ``$T`` is not the same type for which the box was -allocated with ``alloc_existential_box``. +allocated with `alloc_existential_box`_. Blocks ~~~~~~ @@ -5052,7 +5314,7 @@ pointer_to_address Creates an address value corresponding to the ``Builtin.RawPointer`` value ``%0``. Converting a ``RawPointer`` back to an address of the same type as -its originating ``address_to_pointer`` instruction gives back an equivalent +its originating `address_to_pointer`_ instruction gives back an equivalent address. It is undefined behavior to cast the ``RawPointer`` back to any type other than its original address type or `layout compatible types`_. It is also undefined behavior to cast a ``RawPointer`` from a heap object to any @@ -5176,7 +5438,7 @@ ref_to_raw_pointer Converts a heap object reference to a ``Builtin.RawPointer``. The ``RawPointer`` result can be cast back to the originating class type but does not have ownership semantics. It is undefined behavior to cast a ``RawPointer`` from a -heap object reference to an address using ``pointer_to_address``. +heap object reference to an address using `pointer_to_address`_. raw_pointer_to_ref `````````````````` diff --git a/docs/Testing.md b/docs/Testing.md index b8f02d25b725b..7aab4f99c24dd 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -572,17 +572,17 @@ If you're specifically testing the autoreleasing behavior of code, or do not expect code to interact with the Objective-C runtime, it may be OK to use ``if true {}``, but those assumptions should be commented in the test. -#### Enabling/disabling the lldb test whitelist +#### Enabling/disabling the lldb test allowlist -It's possible to enable a whitelist of swift-specific lldb tests to run during +It's possible to enable a allowlist of swift-specific lldb tests to run during PR smoke testing. Note that the default set of tests which run (which includes -tests not in the whitelist) already only includes swift-specific tests. +tests not in the allowlist) already only includes swift-specific tests. -Enabling the whitelist is an option of last-resort to unblock swift PR testing +Enabling the allowlist is an option of last-resort to unblock swift PR testing in the event that lldb test failures cannot be resolved in a timely way. If -this becomes necessary, be sure to double-check that enabling the whitelist +this becomes necessary, be sure to double-check that enabling the allowlist actually unblocks PR testing by running the smoke test build preset locally. -To enable the lldb test whitelist, add `-G swiftpr` to the +To enable the lldb test allowlist, add `-G swiftpr` to the `LLDB_TEST_CATEGORIES` variable in `utils/build-script-impl`. Disable it by removing that option. diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index 0ca8b656501ff..4260e19a78172 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -52,7 +52,6 @@ From the settings application, go to `Update & Security`. In the `For developer 1. Clone `apple/llvm-project` into a directory for the toolchain 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/swift-build` as a peer of the toolchain directory - Currently, other repositories in the Swift project have not been tested and may not be supported. @@ -73,21 +72,12 @@ git clone https://github.com/apple/swift-corelibs-xctest swift-corelibs-xctest git clone https://github.com/apple/swift-llbuild llbuild git clone https://github.com/apple/swift-tools-support-core swift-tools-support-core git clone -c core.autocrlf=input https://github.com/apple/swift-package-manager swiftpm -git clone https://github.com/compnerd/swift-build swift-build ``` -## Acquire ICU, SQLite3, curl, libxml2 and zlib +## Dependencies (ICU, SQLite3, curl, libxml2 and zlib) -``` -C:\Python27\python.exe -m pip install --user msrest azure-devops tabulate -C:\Python27\python.exe swift-build\utilities\swift-build.py --build-id ICU --latest-artifacts --filter windows-x64 --download -C:\Python27\python.exe swift-build\utilities\swift-build.py --build-id XML2 --latest-artifacts --filter windows-x64 --download -C:\Python27\python.exe swift-build\utilities\swift-build.py --build-id CURL --latest-artifacts --filter windows-x64 --download -C:\Python27\python.exe swift-build\utilities\swift-build.py --build-id zlib --latest-artifacts --filter windows-x64 --download -C:\Python27\python.exe swift-build\utilities\swift-build.py --build-id SQLite --latest-artifacts --filter windows-x64 --download -``` - -Extract the zip files, ignoring the top level directory, into `S:/Library`. The directory structure should resemble: +The instructions assume that the dependencies are in `S:/Library`. The directory +structure should resemble: ``` /Library @@ -103,6 +93,10 @@ Extract the zip files, ignoring the top level directory, into `S:/Library`. The ┕ usr/... ``` +Note that only ICU is required for building the toolchain, and SQLite is only +needed for building llbuild and onwards. The ICU project provides binaries, +alternatively, see the ICU project for details on building ICU from source. + ## One-time Setup (re-run on Visual Studio upgrades) Set up the `ucrt`, `visualc`, and `WinSDK` modules by: @@ -126,26 +120,17 @@ Warning: Creating the above links usually requires administrator privileges. The ```cmd cmake -B "S:\b\toolchain" ^ - -C S:\swift-build\cmake\caches\windows-x86_64.cmake ^ - -C S:\swift-build\cmake\caches\org.compnerd.dt.cmake ^ + -C S:\swift\cmake\caches\Windows-x86_64.cmake ^ -D CMAKE_BUILD_TYPE=Release ^ - -D LLVM_ENABLE_ASSERTIONS=YES ^ - -D LLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lldb;lld" ^ - -D LLVM_EXTERNAL_PROJECTS="cmark;swift" ^ -D SWIFT_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch ^ -D LLVM_ENABLE_PDB=YES ^ - -D LLVM_ENABLE_LIBEDIT=NO ^ - -D LLDB_ENABLE_PYTHON=YES ^ - -D LLVM_EXTERNAL_SWIFT_SOURCE_DIR="S:/swift" ^ - -D LLVM_EXTERNAL_CMARK_SOURCE_DIR="S:/cmark" ^ - -D SWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="S:/Library/icu-67/usr/include" ^ - -D SWIFT_WINDOWS_x86_64_ICU_UC="S:/Library/icu-67/usr/lib/icuuc67.lib" ^ - -D SWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="S:/Library/icu-67/usr/include" ^ - -D SWIFT_WINDOWS_x86_64_ICU_I18N="S:/Library/icu-67/usr/lib/icuin67.lib" ^ - -D CMAKE_INSTALL_PREFIX="C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr" ^ - -D PYTHON_EXECUTABLE=C:\Python27\python.exe ^ - -D SWIFT_BUILD_DYNAMIC_STDLIB=YES ^ - -D SWIFT_BUILD_DYNAMIC_SDK_OVERLAY=YES ^ + -D LLVM_EXTERNAL_SWIFT_SOURCE_DIR=S:\swift ^ + -D LLVM_EXTERNAL_CMARK_SOURCE_DIR=S:\cmark ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE=S:\Library\icu-67\usr\include ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC=S:\Library\icu-67\usr\lib\icuuc67.lib ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE=S:\Library\icu-67\usr\include ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N=S:\Library\icu-67\usr\lib\icuin67.lib ^ + -D CMAKE_INSTALL_PREFIX=C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr ^ -G Ninja ^ -S S:\llvm-project\llvm diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index aa5ece97b10c2..2f6d07561224b 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -492,6 +492,8 @@ template class TargetEnumDescriptor; template class TargetStructDescriptor; template struct TargetGenericMetadataPattern; +using TypeContextDescriptor = TargetTypeContextDescriptor; + // FIXME: https://bugs.swift.org/browse/SR-1155 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winvalid-offsetof" @@ -723,6 +725,10 @@ struct TargetMetadata { bool satisfiesClassConstraint() const; + const TypeContextDescriptor *getDescription() const; + + bool isStaticallySpecializedGenericMetadata() const; + bool isCanonicalStaticallySpecializedGenericMetadata() const; #if SWIFT_OBJC_INTEROP @@ -1304,6 +1310,14 @@ struct TargetClassMetadata : public TargetAnyClassMetadata { } #endif + bool isStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + return this->Flags & ClassFlags::IsStaticSpecialization; + } + bool isCanonicalStaticallySpecializedGenericMetadata() const { auto *description = getDescription(); if (!description->isGeneric()) @@ -1449,6 +1463,18 @@ struct TargetStructMetadata : public TargetValueMetadata { return reinterpret_cast(asWords + offset); } + bool isStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isStaticSpecialization(); + } + bool isCanonicalStaticallySpecializedGenericMetadata() const { auto *description = getDescription(); if (!description->isGeneric()) @@ -1525,6 +1551,18 @@ struct TargetEnumMetadata : public TargetValueMetadata { return *asWords; } + bool isStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isStaticSpecialization(); + } + bool isCanonicalStaticallySpecializedGenericMetadata() const { auto *description = getDescription(); if (!description->isGeneric()) @@ -1596,7 +1634,8 @@ struct TargetFunctionTypeMetadata : public TargetMetadata { FunctionMetadataConvention getConvention() const { return Flags.getConvention(); } - bool throws() const { return Flags.throws(); } + bool isAsync() const { return Flags.isAsync(); } + bool isThrowing() const { return Flags.isThrowing(); } bool hasParameterFlags() const { return Flags.hasParameterFlags(); } bool isEscaping() const { return Flags.isEscaping(); } @@ -3820,8 +3859,6 @@ class TargetTypeContextDescriptor } }; -using TypeContextDescriptor = TargetTypeContextDescriptor; - /// Storage for class metadata bounds. This is the variable returned /// by getAddrOfClassMetadataBounds in the compiler. /// diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 6ab98e9e37e56..b5b62a7edf5f0 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -794,8 +794,9 @@ class TargetFunctionTypeFlags { ThrowsMask = 0x01000000U, ParamFlagsMask = 0x02000000U, EscapingMask = 0x04000000U, - DifferentiableMask = 0x08000000U, - LinearMask = 0x10000000U + DifferentiableMask = 0x08000000U, + LinearMask = 0x10000000U, + AsyncMask = 0x20000000U, }; int_type Data; @@ -813,7 +814,13 @@ class TargetFunctionTypeFlags { return TargetFunctionTypeFlags((Data & ~ConventionMask) | (int_type(c) << ConventionShift)); } - + + constexpr TargetFunctionTypeFlags + withAsync(bool async) const { + return TargetFunctionTypeFlags((Data & ~AsyncMask) | + (async ? AsyncMask : 0)); + } + constexpr TargetFunctionTypeFlags withThrows(bool throws) const { return TargetFunctionTypeFlags((Data & ~ThrowsMask) | @@ -847,10 +854,10 @@ class TargetFunctionTypeFlags { FunctionMetadataConvention getConvention() const { return FunctionMetadataConvention((Data&ConventionMask) >> ConventionShift); } - - bool throws() const { - return bool(Data & ThrowsMask); - } + + bool isAsync() const { return bool(Data & AsyncMask); } + + bool isThrowing() const { return bool(Data & ThrowsMask); } bool isEscaping() const { return bool (Data & EscapingMask); diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 44aff1b7cd009..e3745be52d7b2 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -553,6 +553,9 @@ class ASTContext final { /// Array.reserveCapacityForAppend(newElementsCount: Int) FuncDecl *getArrayReserveCapacityDecl() const; + /// Retrieve the declaration of String.init(_builtinStringLiteral ...) + ConstructorDecl *getMakeUTF8StringDecl() const; + // Retrieve the declaration of Swift._stdlib_isOSVersionAtLeast. FuncDecl *getIsOSVersionAtLeastDecl() const; @@ -587,13 +590,9 @@ class ASTContext final { /// /// \param params The function parameters. /// \param resultTy The Swift result type. - /// \param incompleteExtInfo Used to convey escaping and throwing - /// information, in case it is needed. /// \param trueRep The actual calling convention, which must be C-compatible. - /// The calling convention in \p incompleteExtInfo is ignored. const clang::Type * getClangFunctionType(ArrayRef params, Type resultTy, - const FunctionType::ExtInfo incompleteExtInfo, FunctionTypeRepresentation trueRep); /// Get the Swift declaration that a Clang declaration was exported from, @@ -658,6 +657,10 @@ class ASTContext final { /// platform. AvailabilityContext getCompareProtocolConformanceDescriptorsAvailability(); + /// Get the runtime availability of support for inter-module prespecialized + /// generic metadata. + AvailabilityContext getIntermodulePrespecializedGenericMetadataAvailability(); + /// Get the runtime availability of features introduced in the Swift 5.2 /// compiler for the target platform. AvailabilityContext getSwift52Availability(); diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index f8582c5e8c41b..f527b6a0d49b7 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -92,8 +92,7 @@ class ASTBuilder { Type createBoundGenericType(GenericTypeDecl *decl, ArrayRef args, Type parent); - Type createTupleType(ArrayRef eltTypes, StringRef labels, - bool isVariadic); + Type createTupleType(ArrayRef eltTypes, StringRef labels); Type createFunctionType(ArrayRef> params, Type output, FunctionTypeFlags flags); diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index d806bcc3fda0c..50c401e7ee2bc 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -213,6 +213,9 @@ class ASTMangler : public Mangler { std::string mangleTypeForDebugger(Type decl, const DeclContext *DC); + /// Create a mangled name to be used for _typeName constant propagation. + std::string mangleTypeForTypeName(Type type); + std::string mangleOpaqueTypeDescriptor(const OpaqueTypeDecl *decl); std::string mangleDeclType(const ValueDecl *decl); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 84bae062b3564..0cac9c8e61277 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -195,7 +195,7 @@ class ASTScopeImpl { /// Get ride of descendants and remove them from scopedNodes so the scopes /// can be recreated. Needed because typechecking inserts a return statment - /// into intiailizers. + /// into initializers. void disownDescendants(ScopeCreator &); public: // for addReusedBodyScopes @@ -415,10 +415,17 @@ class ASTScopeImpl { unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, const DeclContext *startingContext, DeclConsumer); + /// Entry point into ASTScopeImpl-land for labeled statement lookups. + static llvm::SmallVector + lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc); + static Optional computeIsCascadingUse(ArrayRef history, Optional initialIsCascadingUse); + static std::pair + lookupFallthroughSourceAndDest(SourceFile *sourceFile, SourceLoc loc); + #pragma mark - - lookup- starting point private: static const ASTScopeImpl *findStartingScopeForLookup(SourceFile *, @@ -538,6 +545,12 @@ class ASTScopeImpl { NullablePtr ancestorWithDeclSatisfying(function_ref predicate) const; + + /// Whether this scope terminates lookup of labeled statements in the + /// children below it, because one cannot perform a "break" or a "continue" + /// in a child that goes outside of this scope. + virtual bool isLabeledStmtLookupTerminator() const; + }; // end of ASTScopeImpl #pragma mark - specific scope classes @@ -715,6 +728,10 @@ class GenericTypeOrExtensionWherePortion final GenericTypeOrExtensionWherePortion() : GenericTypeOrExtensionWhereOrBodyPortion("Where") {} + bool lookupMembersOf(const GenericTypeOrExtensionScope *scope, + ArrayRef, + ASTScopeImpl::DeclConsumer consumer) const override; + ASTScopeImpl *expandScope(GenericTypeOrExtensionScope *, ScopeCreator &) const override; @@ -815,6 +832,8 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl { return nullptr; } + bool areMembersVisibleFromWhereClause() const; + virtual void createBodyScope(ASTScopeImpl *leaf, ScopeCreator &) {} protected: @@ -1351,6 +1370,9 @@ class ConditionalClauseScope final : public ASTScopeImpl { private: ArrayRef getCond() const; const StmtConditionElement &getStmtConditionElement() const; + +protected: + bool isLabeledStmtLookupTerminator() const override; }; /// If, while, & guard statements all start with a conditional clause, then some @@ -1374,6 +1396,7 @@ class ConditionalClausePatternUseScope final : public ASTScopeImpl { bool lookupLocalsOrMembers(ArrayRef, DeclConsumer) const override; void printSpecifics(llvm::raw_ostream &out) const override; + bool isLabeledStmtLookupTerminator() const override; }; @@ -1707,6 +1730,9 @@ class AbstractStmtScope : public ASTScopeImpl { virtual Stmt *getStmt() const = 0; NullablePtr getStmtIfAny() const override { return getStmt(); } NullablePtr getReferrent() const override; + +protected: + bool isLabeledStmtLookupTerminator() const override; }; class LabeledConditionalStmtScope : public AbstractStmtScope { @@ -1714,10 +1740,6 @@ class LabeledConditionalStmtScope : public AbstractStmtScope { Stmt *getStmt() const override; virtual LabeledConditionalStmt *getLabeledConditionalStmt() const = 0; - /// If a condition is present, create the martuska. - /// Return the lookupParent for the use scope. - ASTScopeImpl *createCondScopes(); - protected: /// Return the lookupParent required to search these. ASTScopeImpl *createNestedConditionalClauseScopes(ScopeCreator &, @@ -1801,6 +1823,7 @@ class LookupParentDiversionScope final : public ASTScopeImpl { NullablePtr getLookupParent() const override { return lookupParent; } + bool isLabeledStmtLookupTerminator() const override; }; class RepeatWhileScope final : public AbstractStmtScope { @@ -1820,6 +1843,23 @@ class RepeatWhileScope final : public AbstractStmtScope { Stmt *getStmt() const override { return stmt; } }; +class DoStmtScope final : public AbstractStmtScope { +public: + DoStmt *const stmt; + DoStmtScope(DoStmt *e) : stmt(e) {} + virtual ~DoStmtScope() {} + +protected: + ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + +private: + void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); + +public: + std::string getClassName() const override; + Stmt *getStmt() const override { return stmt; } +}; + class DoCatchStmtScope final : public AbstractStmtScope { public: DoCatchStmt *const stmt; @@ -1891,6 +1931,7 @@ class ForEachPatternScope final : public ASTScopeImpl { protected: bool lookupLocalsOrMembers(ArrayRef, DeclConsumer) const override; + bool isLabeledStmtLookupTerminator() const override; }; class CaseStmtScope final : public AbstractStmtScope { diff --git a/include/swift/AST/ASTWalker.h b/include/swift/AST/ASTWalker.h index 4aed8fa6eabf0..f45f84681616c 100644 --- a/include/swift/AST/ASTWalker.h +++ b/include/swift/AST/ASTWalker.h @@ -25,7 +25,6 @@ class ModuleDecl; class Stmt; class Pattern; class TypeRepr; -class TypeLoc; class ParameterList; enum class AccessKind: unsigned char; @@ -177,19 +176,6 @@ class ASTWalker { /// returns failure. virtual bool walkToDeclPost(Decl *D) { return true; } - /// This method is called when first visiting a TypeLoc, before - /// walking into its TypeRepr children. If it returns false, the subtree is - /// skipped. - /// - /// \param TL The TypeLoc to check. - virtual bool walkToTypeLocPre(TypeLoc &TL) { return true; } - - /// This method is called after visiting the children of a TypeLoc. - /// If it returns false, the remaining traversal is terminated and returns - /// failure. - virtual bool walkToTypeLocPost(TypeLoc &TL) { return true; } - - /// This method is called when first visiting a TypeRepr, before /// walking into its children. If it returns false, the subtree is skipped. /// @@ -229,6 +215,11 @@ class ASTWalker { /// TapExpr. virtual bool shouldWalkIntoTapExpression() { return true; } + /// This method configures whether the walker should visit the capture + /// initializer expressions within a capture list directly, rather than + /// walking the declarations. + virtual bool shouldWalkCaptureInitializerExpressions() { return false; } + /// This method configures whether the walker should exhibit the legacy /// behavior where accessors appear as peers of their storage, rather /// than children nested inside of it. diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index da5be7c7adf91..4721c2971a28d 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -53,6 +53,20 @@ class AnyFunctionRef { } } + /// Construct an AnyFunctionRef from a decl context that might be + /// some sort of function. + static Optional fromDeclContext(DeclContext *dc) { + if (auto fn = dyn_cast(dc)) { + return AnyFunctionRef(fn); + } + + if (auto ace = dyn_cast(dc)) { + return AnyFunctionRef(ace); + } + + return None; + } + CaptureInfo getCaptureInfo() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getCaptureInfo(); diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index fb1085509c8f3..b6fea6459d533 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -244,7 +244,7 @@ SIMPLE_DECL_ATTR(nonobjc, NonObjC, ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 30) SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout, - OnVar | OnClass | OnStruct | + OnVar | OnClass | OnStruct | OnProtocol | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, 31) diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 18524c9933e9e..6dc1bc0aaaefe 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -649,6 +649,29 @@ bool getBuiltinDifferentiableOrLinearFunctionConfig( bool getBuiltinDifferentiableOrLinearFunctionConfig( StringRef operationName, unsigned &arity, bool &throws); +/// Returns the SIL differentiability witness generic signature given the +/// original declaration's generic signature and the derivative generic +/// signature. +/// +/// In general, the differentiability witness generic signature is equal to the +/// derivative generic signature. +/// +/// Edge case, if two conditions are satisfied: +/// 1. The derivative generic signature is equal to the original generic +/// signature. +/// 2. The derivative generic signature has *all concrete* generic parameters +/// (i.e. all generic parameters are bound to concrete types via same-type +/// requirements). +/// +/// Then the differentiability witness generic signature is `nullptr`. +/// +/// Both the original and derivative declarations are lowered to SIL functions +/// with a fully concrete type and no generic signature, so the +/// differentiability witness should similarly have no generic signature. +GenericSignature +getDifferentiabilityWitnessGenericSignature(GenericSignature origGenSig, + GenericSignature derivativeGenSig); + } // end namespace autodiff } // end namespace swift diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index cd4aa5b46a5b4..7d15824bd4684 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -701,6 +701,9 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special) // TypePtrAuthDiscriminator has type (T.Type) -> Int64 BUILTIN_MISC_OPERATION(TypePtrAuthDiscriminator, "typePtrAuthDiscriminator", "n", Special) +// int_instrprof_increment has type (Builtin.RawPointer, Builtin.Int64, Builtin.Int32, Builtin.Int32) -> (). +BUILTIN_MISC_OPERATION(IntInstrprofIncrement, "int_instrprof_increment", "", Special) + // BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are // specially emitted during SIL generation. // diff --git a/include/swift/AST/CMakeLists.txt b/include/swift/AST/CMakeLists.txt deleted file mode 100644 index ebc60523cb0d8..0000000000000 --- a/include/swift/AST/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -swift_install_in_component(DIRECTORY diagnostics - DESTINATION "share/swift/" - COMPONENT compiler) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 8848b5c2360a1..e3fb02109d10b 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -385,7 +385,7 @@ class alignas(1 << DeclAlignInBits) Decl { SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2, StaticSpelling : 2 ); - SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1+1, /// \see AbstractFunctionDecl::BodyKind BodyKind : 3, @@ -398,6 +398,9 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether we are overridden later. Overridden : 1, + /// Whether the function is async. + Async : 1, + /// Whether the function body throws. Throws : 1, @@ -554,14 +557,14 @@ class alignas(1 << DeclAlignInBits) Decl { IsIncompatibleWithWeakReferences : 1 ); - SWIFT_INLINE_BITFIELD(StructDecl, NominalTypeDecl, 1+1, - /// True if this struct has storage for fields that aren't accessible in - /// Swift. - HasUnreferenceableStorage : 1, - /// True if this struct is imported from C++ and not trivially copyable. - IsCxxNotTriviallyCopyable : 1 - ); - + SWIFT_INLINE_BITFIELD( + StructDecl, NominalTypeDecl, 1 + 1, + /// True if this struct has storage for fields that aren't accessible in + /// Swift. + HasUnreferenceableStorage : 1, + /// True if this struct is imported from C++ and does not have trivial value witness functions. + IsCxxNonTrivial : 1); + SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+1, /// True if the enum has cases and at least one case has associated values. HasAssociatedValues : 2, @@ -1190,9 +1193,6 @@ class GenericParamList final : GenericParamList *OuterParameters; - SourceLoc TrailingWhereLoc; - unsigned FirstTrailingWhereArg; - GenericParamList(SourceLoc LAngleLoc, ArrayRef Params, SourceLoc WhereLoc, @@ -1272,31 +1272,6 @@ class GenericParamList final : /// implicitly-generated requirements, and may be non-empty even if no /// 'where' keyword is present. ArrayRef getRequirements() const { return Requirements; } - - /// Retrieve only those requirements that are written within the brackets, - /// which does not include any requirements written in a trailing where - /// clause. - ArrayRef getNonTrailingRequirements() const { - return Requirements.slice(0, FirstTrailingWhereArg); - } - - /// Retrieve only those requirements written in a trailing where - /// clause. - ArrayRef getTrailingRequirements() const { - return Requirements.slice(FirstTrailingWhereArg); - } - - /// Determine whether the generic parameters have a trailing where clause. - bool hasTrailingWhereClause() const { - return FirstTrailingWhereArg < Requirements.size(); - } - - /// Add a trailing 'where' clause to the list of requirements. - /// - /// Trailing where clauses are written outside the angle brackets, after the - /// main part of a declaration's signature. - void addTrailingWhereClause(ASTContext &ctx, SourceLoc trailingWhereLoc, - ArrayRef trailingRequirements); /// Retrieve the outer generic parameter list. /// @@ -1309,6 +1284,8 @@ class GenericParamList final : /// for more information. void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } + void setDeclContext(DeclContext *dc); + SourceLoc getLAngleLoc() const { return Brackets.Start; } SourceLoc getRAngleLoc() const { return Brackets.End; } @@ -1319,19 +1296,10 @@ class GenericParamList final : if (WhereLoc.isInvalid()) return SourceRange(); - auto endLoc = Requirements[FirstTrailingWhereArg-1].getSourceRange().End; + auto endLoc = Requirements.back().getSourceRange().End; return SourceRange(WhereLoc, endLoc); } - /// Retrieve the source range covering the trailing where clause. - SourceRange getTrailingWhereClauseSourceRange() const { - if (!hasTrailingWhereClause()) - return SourceRange(); - - return SourceRange(TrailingWhereLoc, - Requirements.back().getSourceRange().End); - } - /// Configure the depth of the generic parameters in this list. void setDepth(unsigned depth); @@ -1413,6 +1381,11 @@ class GenericContext : private _GenericContext, public DeclContext { /// this context is not generic. GenericParamList *getGenericParams() const; + /// Retrieve the generic parameters as written in source. Unlike + /// getGenericParams() this will not synthesize generic parameters for + /// extensions, protocols and certain type aliases. + GenericParamList *getParsedGenericParams() const; + /// Determine whether this context has generic parameters /// of its own. /// @@ -3013,6 +2986,11 @@ class TypeAliasDecl : public GenericTypeDecl { Type getCachedUnderlyingType() const { return UnderlyingTy.getType(); } /// For generic typealiases, return the unbound generic type. + /// + /// Since UnboundGenericType is on its way out, so is this method. Try to + /// avoid introducing new callers if possible. Instead of passing around + /// an UnboundGenericType, considering passing around the Decl itself + /// instead. UnboundGenericType *getUnboundGenericType() const; /// Retrieve a sugared interface type containing the structure of the interface @@ -3399,6 +3377,11 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// getDeclaredType - Retrieve the type declared by this entity, without /// any generic parameters bound if this is a generic type. + /// + /// Since UnboundGenericType is on its way out, so is this method. Try to + /// avoid introducing new callers if possible. Instead of passing around + /// an UnboundGenericType, considering passing around the Decl itself + /// instead. Type getDeclaredType() const; /// getDeclaredInterfaceType - Retrieve the type declared by this entity, with @@ -3789,13 +3772,9 @@ class StructDecl final : public NominalTypeDecl { Bits.StructDecl.HasUnreferenceableStorage = v; } - bool isCxxNotTriviallyCopyable() const { - return Bits.StructDecl.IsCxxNotTriviallyCopyable; - } + bool isCxxNonTrivial() const { return Bits.StructDecl.IsCxxNonTrivial; } - void setIsCxxNotTriviallyCopyable(bool v) { - Bits.StructDecl.IsCxxNotTriviallyCopyable = v; - } + void setIsCxxNonTrivial(bool v) { Bits.StructDecl.IsCxxNonTrivial = v; } }; /// This is the base type for AncestryOptions. Each flag describes possible @@ -4327,11 +4306,6 @@ class ProtocolDecl final : public NominalTypeDecl { /// protocol. bool inheritsFrom(const ProtocolDecl *Super) const; - ProtocolType *getDeclaredType() const { - return reinterpret_cast( - NominalTypeDecl::getDeclaredType().getPointer()); - } - SourceLoc getStartLoc() const { return ProtocolLoc; } SourceRange getSourceRange() const { return SourceRange(ProtocolLoc, getBraces().End); @@ -5221,10 +5195,6 @@ class VarDecl : public AbstractStorageDecl { /// a suitable `init(wrappedValue:)`. bool isPropertyMemberwiseInitializedWithWrappedType() const; - /// Whether the innermost property wrapper's initializer's 'wrappedValue' parameter - /// is marked with '@autoclosure' and '@escaping'. - bool isInnermostPropertyWrapperInitUsesEscapingAutoClosure() const; - /// Return the interface type of the value used for the 'wrappedValue:' /// parameter when initializing a property wrapper. /// @@ -5826,6 +5796,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { CaptureInfo Captures; + /// Location of the 'async' token. + SourceLoc AsyncLoc; + /// Location of the 'throws' token. SourceLoc ThrowsLoc; @@ -5835,15 +5808,17 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { } LazySemanticInfo = { }; AbstractFunctionDecl(DeclKind Kind, DeclContext *Parent, DeclName Name, - SourceLoc NameLoc, bool Throws, SourceLoc ThrowsLoc, + SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, + bool Throws, SourceLoc ThrowsLoc, bool HasImplicitSelfDecl, GenericParamList *GenericParams) : GenericContext(DeclContextKind::AbstractFunctionDecl, Parent, GenericParams), ValueDecl(Kind, Parent, Name, NameLoc), - Body(nullptr), ThrowsLoc(ThrowsLoc) { + Body(nullptr), AsyncLoc(AsyncLoc), ThrowsLoc(ThrowsLoc) { setBodyKind(BodyKind::None); Bits.AbstractFunctionDecl.HasImplicitSelfDecl = HasImplicitSelfDecl; Bits.AbstractFunctionDecl.Overridden = false; + Bits.AbstractFunctionDecl.Async = Async; Bits.AbstractFunctionDecl.Throws = Throws; Bits.AbstractFunctionDecl.Synthesized = false; Bits.AbstractFunctionDecl.HasSingleExpressionBody = false; @@ -5903,9 +5878,16 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { Bits.AbstractFunctionDecl.IAMStatus = newValue.getRawValue(); } + /// Retrieve the location of the 'async' keyword, if present. + SourceLoc getAsyncLoc() const { return AsyncLoc; } + /// Retrieve the location of the 'throws' keyword, if present. SourceLoc getThrowsLoc() const { return ThrowsLoc; } + /// Returns true if the function is marked as `async`. The + /// type of the function will be `async` as well. + bool hasAsync() const { return Bits.AbstractFunctionDecl.Async; } + /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } @@ -6151,11 +6133,13 @@ class FuncDecl : public AbstractFunctionDecl { SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, bool HasImplicitSelfDecl, GenericParamList *GenericParams, DeclContext *Parent) : AbstractFunctionDecl(Kind, Parent, Name, NameLoc, + Async, AsyncLoc, Throws, ThrowsLoc, HasImplicitSelfDecl, GenericParams), StaticLoc(StaticLoc), FuncLoc(FuncLoc) { @@ -6177,6 +6161,7 @@ class FuncDecl : public AbstractFunctionDecl { StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, DeclContext *Parent, @@ -6202,6 +6187,7 @@ class FuncDecl : public AbstractFunctionDecl { StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, DeclContext *Parent); @@ -6210,6 +6196,7 @@ class FuncDecl : public AbstractFunctionDecl { StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, ParameterList *ParameterList, @@ -6351,7 +6338,8 @@ class AccessorDecl final : public FuncDecl { : FuncDecl(DeclKind::Accessor, staticLoc, staticSpelling, /*func loc*/ declLoc, /*name*/ Identifier(), /*name loc*/ declLoc, - throws, throwsLoc, hasImplicitSelfDecl, genericParams, parent), + /*Async=*/false, SourceLoc(), throws, throwsLoc, + hasImplicitSelfDecl, genericParams, parent), AccessorKeywordLoc(accessorKeywordLoc), Storage(storage) { Bits.AccessorDecl.AccessorKind = unsigned(accessorKind); diff --git a/include/swift/AST/DefaultArgumentKind.h b/include/swift/AST/DefaultArgumentKind.h index f687bb4426eac..11aa4f79b0897 100644 --- a/include/swift/AST/DefaultArgumentKind.h +++ b/include/swift/AST/DefaultArgumentKind.h @@ -36,18 +36,6 @@ enum class DefaultArgumentKind : uint8_t { /// The default argument is inherited from the corresponding argument of the /// overridden declaration. Inherited, - /// The #file default argument, which is expanded at the call site. - File, - /// The #filePath default argument, which is expanded at the call site. - FilePath, - /// The #line default argument, which is expanded at the call site. - Line, - /// The #column default argument, which is expanded at the call site. - Column, - /// The #function default argument, which is expanded at the call site. - Function, - /// The #dsohandle default argument, which is expanded at the call site. - DSOHandle, /// The "nil" literal. NilLiteral, /// An empty array literal. @@ -56,8 +44,11 @@ enum class DefaultArgumentKind : uint8_t { EmptyDictionary, /// A reference to the stored property. This is a special default argument /// kind for the synthesized memberwise constructor to emit a call to the - // property's initializer. + /// property's initializer. StoredProperty, + // Magic identifier literals expanded at the call site: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) NAME, +#include "swift/AST/MagicIdentifierKinds.def" }; enum { NumDefaultArgumentKindBits = 4 }; diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index ef9160641981e..ee3c776cd9db6 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -24,6 +24,8 @@ #include "swift/AST/TypeLoc.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" namespace swift { @@ -742,8 +744,26 @@ namespace swift { void setLocalization(std::string locale, std::string path) { assert(!locale.empty()); assert(!path.empty()); - localization = - std::make_unique(locale, path); + llvm::SmallString<128> filePath(path); + llvm::sys::path::append(filePath, locale); + llvm::sys::path::replace_extension(filePath, ".db"); + + // If the serialized diagnostics file not available, + // fallback to the `YAML` file. + if (llvm::sys::fs::exists(filePath)) { + if (auto file = llvm::MemoryBuffer::getFile(filePath)) { + localization = std::make_unique( + std::move(file.get())); + } + } else { + llvm::sys::path::replace_extension(filePath, ".yaml"); + // In case of missing localization files, we should fallback to messages + // from `.def` files. + if (llvm::sys::fs::exists(filePath)) { + localization = + std::make_unique(filePath.str()); + } + } } void ignoreDiagnostic(DiagID id) { diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index d797125013ed3..cdeef09e3058e 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -118,14 +118,22 @@ ERROR(sdk_node_unrecognized_decl_kind,none, ERROR(sdk_node_unrecognized_accessor_kind,none, "unrecognized accessor kind '%0' in SDK node", (StringRef)) -// Emitted from ModuleDecl::computeMagicFileStringMap() -WARNING(pound_source_location_creates_pound_file_conflicts,none, - "'#sourceLocation' directive produces '#file' string of '%0', which " - "conflicts with '#file' strings produced by other paths in the module", - (StringRef)) +// Emitted from ModuleDecl::computeFileIDMap() +WARNING(source_location_creates_file_id_conflicts,none, + "'#sourceLocation' directive produces '#fileID' string of '%0', which " + "conflicts with '#fileID' strings produced by other paths in the " + "module", (StringRef)) NOTE(fixit_correct_source_location_file,none, "change file in '#sourceLocation' to '%0'", (StringRef)) +// Usually, but not always, emitted from the driver +ERROR(error_two_files_same_name,none, + "filename \"%0\" used twice: '%1' and '%2'", + (StringRef, StringRef, StringRef)) +NOTE(note_explain_two_files_same_name,none, + "filenames are used to distinguish private declarations with the same " + "name", ()) + //------------------------------------------------------------------------------ // MARK: Circular reference diagnostics //------------------------------------------------------------------------------ @@ -170,5 +178,12 @@ WARNING(cross_imported_by_both_modules, none, "please report this bug to the maintainers of these modules", (Identifier, Identifier, Identifier)) +//------------------------------------------------------------------------------ +// MARK: dependencies scanner diagnostics +//------------------------------------------------------------------------------ + +ERROR(scanner_find_cycle, none, + "dependency scanner detected dependency cycle: '%0'", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index b6b6a3cd1e484..17b5b0d4c36ea 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -104,13 +104,6 @@ WARNING(warn_arclite_not_found_when_link_objc_runtime,none, "unable to find Objective-C runtime support library 'arclite'; " "pass '-no-link-objc-runtime' to silence this warning", ()) -ERROR(error_two_files_same_name,none, - "filename \"%0\" used twice: '%1' and '%2'", - (StringRef, StringRef, StringRef)) -NOTE(note_explain_two_files_same_name,none, - "filenames are used to distinguish private declarations with the same " - "name", ()) - WARNING(warn_cannot_stat_input,none, "unable to determine when '%0' was last modified: %1", (StringRef, StringRef)) @@ -129,6 +122,9 @@ ERROR(error_conflicting_options, none, ERROR(error_option_not_supported, none, "'%0' is not supported with '%1'", (StringRef, StringRef)) +ERROR(error_requirement_not_met, none, + "'%0' requires '%1'", + (StringRef, StringRef)) WARNING(warn_ignore_embed_bitcode, none, "ignoring -embed-bitcode since no object file is being generated", ()) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 00a77b853cadf..6f88d42b6b517 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -262,6 +262,14 @@ ERROR(explicit_swift_module_map_corrupted,none, "explicit Swift module map from %0 is malformed", (StringRef)) +ERROR(placeholder_dependency_module_map_missing,none, + "cannot open Swift placeholder dependency module map from %0", + (StringRef)) + +ERROR(placeholder_dependency_module_map_corrupted,none, + "Swift placeholder dependency module map from %0 is malformed", + (StringRef)) + REMARK(default_previous_install_name, none, "default previous install name for %0 is %1", (StringRef, StringRef)) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 1a79bcc0da34d..6c7f4e6533b04 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -112,8 +112,8 @@ ERROR(lex_single_quote_string,none, ERROR(lex_invalid_curly_quote,none, "unicode curly quote found, replace with '\"'", ()) NOTE(lex_confusable_character,none, - "unicode character '%0' looks similar to '%1'; did you mean to use '%1'?", - (StringRef, StringRef)) + "unicode character '%0' (%1) looks similar to '%2' (%3); did you mean to use '%2' (%3)?", + (StringRef, StringRef, StringRef, StringRef)) WARNING(lex_nonbreaking_space,none, "non-breaking space (U+00A0) used instead of regular space", ()) @@ -727,10 +727,9 @@ ERROR(generic_non_function,PointsToFirstBadToken, ERROR(rethrowing_function_type,none, "only function declarations may be marked 'rethrows'; " "did you mean 'throws'?", ()) -ERROR(throws_in_wrong_position,none, - "'throws' may only occur before '->'", ()) -ERROR(rethrows_in_wrong_position,none, - "'rethrows' may only occur before '->'", ()) +ERROR(async_or_throws_in_wrong_position,none, + "%select{'throws'|'rethrows'|'async'}0 may only occur before '->'", + (unsigned)) ERROR(throw_in_function_type,none, "expected throwing specifier; did you mean 'throws'?", ()) ERROR(expected_type_before_arrow,none, @@ -742,6 +741,9 @@ ERROR(function_type_argument_label,none, (Identifier)) ERROR(expected_dynamic_func_attr,none, "expected a dynamically_replaceable function", ()) +ERROR(async_after_throws,none, + "'async' must precede %select{'throws'|'rethrows'}0", (bool)) +ERROR(async_init,none, "initializer cannot be marked 'async'", ()) // Enum Types ERROR(expected_expr_enum_case_raw_value,PointsToFirstBadToken, @@ -1254,6 +1256,10 @@ ERROR(expected_colon_after_if_question,none, "expected ':' after '? ...' in ternary expression", ()) ERROR(expected_expr_after_if_colon,none, "expected expression after '? ... :' in ternary expression", ()) +ERROR(expected_expr_after_try, none, + "expected expression after 'try'", ()) +ERROR(expected_expr_after_await, none, + "expected expression after 'await'", ()) // Cast expressions ERROR(expected_type_after_is,none, diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 047a9b3366fe0..36bd43a94f106 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -536,6 +536,7 @@ ERROR(non_borrowed_indirect_addressof,none, REMARK(opt_remark_passed, none, "%0", (StringRef)) REMARK(opt_remark_missed, none, "%0", (StringRef)) +NOTE(opt_remark_note, none, "%0", (StringRef)) // Float-point to integer conversions ERROR(float_to_int_overflow, none, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 4425b27c80adf..67d2f281c6ed0 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -832,6 +832,9 @@ NOTE(confusable_character,none, "%select{identifier|operator}0 '%1' contains possibly confused characters; " "did you mean to use '%2'?", (bool, StringRef, StringRef)) +NOTE(single_confusable_character,none, + "%select{identifier|operator}0 '%1' (%2) looks similar to '%3' (%4); did you mean '%3' (%4)?", + (bool, StringRef, StringRef, StringRef, StringRef)) ERROR(cannot_find_type_in_scope,none, "cannot find type %0 in scope", (DeclNameRef)) ERROR(cannot_find_type_in_scope_did_you_mean,none, @@ -1077,7 +1080,9 @@ NOTE(optional_base_chain,none, "base values", (DeclNameRef)) NOTE(optional_base_remove_optional_for_keypath_root, none, "use unwrapped type %0 as key path root", (Type)) - +NOTE(optional_keypath_application_base, none, + "use '?' to access key path subscript only for non-'nil' base values", ()) + ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " "or chain with '?'?", @@ -1184,6 +1189,12 @@ ERROR(extra_trailing_closure_in_call,none, ERROR(trailing_closure_bad_param,none, "trailing closure passed to parameter of type %0 that does not " "accept a closure", (Type)) +WARNING(unlabeled_trailing_closure_deprecated,none, + "backward matching of the unlabeled trailing closure is deprecated; label the argument with %0 to suppress this warning", + (Identifier)) +NOTE(decl_multiple_defaulted_closure_parameters,none, + "%0 contains defaulted closure parameters %1 and %2", + (DeclName, Identifier, Identifier)) NOTE(candidate_with_extraneous_args,none, "candidate %0 requires %1 argument%s1, " "but %2 %select{were|was}3 %select{provided|used in closure body}4", @@ -2156,6 +2167,9 @@ NOTE(protocol_witness_settable_conflict,none, "candidate is not settable, but protocol requires it", ()) NOTE(protocol_witness_rethrows_conflict,none, "candidate is not 'rethrows', but protocol requires it", ()) +NOTE(protocol_witness_async_conflict,none, + "candidate is %select{not |}0'async', but protocol requirement is%select{| not}0", + (bool)) NOTE(protocol_witness_throws_conflict,none, "candidate throws, but protocol does not allow it", ()) NOTE(protocol_witness_not_objc,none, @@ -2413,6 +2427,9 @@ NOTE(multiple_override_prev,none, ERROR(override_unavailable, none, "cannot override %0 which has been marked unavailable%select{|: %1}1", (DeclBaseName, StringRef)) +NOTE(suggest_removing_override, none, + "remove 'override' modifier to declare a new %0", + (DeclBaseName)) ERROR(override_less_available,none, "overriding %0 must be as available as declaration it overrides", @@ -2437,14 +2454,14 @@ ERROR(override_of_non_open,none, (DescriptiveDeclKind)) ERROR(method_does_not_override,none, - "method does not override any method from its superclass", ()) + "method does not override any method from its %select{parent protocol|superclass}0", (bool)) ERROR(property_does_not_override,none, - "property does not override any property from its superclass", ()) + "property does not override any property from its %select{parent protocol|superclass}0", (bool)) ERROR(subscript_does_not_override,none, - "subscript does not override any subscript from its superclass", ()) + "subscript does not override any subscript from its %select{parent protocol|superclass}0", (bool)) ERROR(initializer_does_not_override,none, "initializer does not override a designated initializer from its " - "superclass", ()) + "%select{parent protocol|superclass}0", (bool)) ERROR(failable_initializer_override,none, "failable initializer %0 cannot override a non-failable initializer", (DeclName)) @@ -2695,6 +2712,12 @@ ERROR(decl_from_hidden_module,none, "it is an SPI imported from %3|" "it is SPI}4", (DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned)) +WARNING(decl_from_hidden_module_warn,none, + "cannot use %0 %1 %select{in SPI|as property wrapper in SPI|" + "in an extension with public or '@usableFromInline' members|" + "in an extension with conditional conformances}2; " + "%select{%3 has been imported as implementation-only}4", + (DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned)) ERROR(conformance_from_implementation_only_module,none, "cannot use conformance of %0 to %1 %select{here|as property wrapper here|" "in an extension with public or '@usableFromInline' members|" @@ -2793,7 +2816,7 @@ WARNING(differentiable_let_property_implicit_noderivative_fixit,none, NOTE(codable_extraneous_codingkey_case_here,none, "CodingKey case %0 does not match any stored properties", (Identifier)) NOTE(codable_non_conforming_property_here,none, - "cannot automatically synthesize %0 because %1 does not conform to %0", (Type, Type)) + "cannot automatically synthesize %0 because %1 does not conform to %0", (Type, TypeLoc)) NOTE(codable_non_decoded_property_here,none, "cannot automatically synthesize %0 because %1 does not have a matching CodingKey and does not have a default value", (Type, Identifier)) NOTE(codable_codingkeys_type_is_not_an_enum_here,none, @@ -2833,9 +2856,12 @@ NOTE(missing_member_type_conformance_prevents_synthesis, none, "protocol %2, preventing synthesized conformance " "of %3 to %2", (unsigned, Type, Type, Type)) -NOTE(classes_automatic_protocol_synthesis,none, - "automatic synthesis of '%0' is not supported for classes", - (StringRef)) +NOTE(automatic_protocol_synthesis_unsupported,none, + "automatic synthesis of '%0' is not supported for %select{classes|structs}1", + (StringRef, unsigned)) +NOTE(comparable_synthesis_raw_value_not_allowed, none, + "enum declares raw type %0, preventing synthesized conformance of %1 to %2", + (Type, Type, Type)) // Dynamic Self ERROR(dynamic_self_non_method,none, @@ -2998,6 +3024,8 @@ ERROR(lazy_must_be_property,none, "lazy is only valid for members of a struct or class", ()) ERROR(lazy_not_strong,none, "lazy properties cannot be %0", (ReferenceOwnership)) +ERROR(lazy_var_storage_access,none, + "access to the underlying storage of a lazy property is not allowed", ()) // Debugger function attribute. ERROR(attr_for_debugger_support_only,none, @@ -3298,7 +3326,7 @@ ERROR(missing_builtin_precedence_group,none, (Identifier)) // If you change this, also change enum TryKindForDiagnostics. -#define TRY_KIND_SELECT(SUB) "%select{try|try!|try?}" #SUB +#define TRY_KIND_SELECT(SUB) "%select{try|try!|try?|await}" #SUB ERROR(try_rhs,none, "'" TRY_KIND_SELECT(0) "' cannot appear to the right of a " @@ -3997,6 +4025,10 @@ NOTE(note_error_to_optional,none, "did you mean to handle error as optional value?", ()) NOTE(note_disable_error_propagation,none, "did you mean to disable error propagation?", ()) +ERROR(async_call_without_await,none, + "call is 'async' but is not marked with 'await'", ()) +WARNING(no_async_in_await,none, + "no calls to 'async' functions occur within 'await' expression", ()) WARNING(no_throw_in_try,none, "no calls to throwing functions occur within 'try' expression", ()) @@ -4358,6 +4390,10 @@ NOTE(not_objc_generic_type_param,none, NOTE(not_objc_function_type_param,none, "function types cannot be represented in Objective-C unless their " "parameters and returns can be", ()) +ERROR(not_objc_function_async,none, + "'async' function cannot be represented in Objective-C", ()) +NOTE(not_objc_function_type_async,none, + "'async' function types cannot be represented in Objective-C", ()) NOTE(not_objc_function_type_throwing,none, "throwing function types cannot be represented in Objective-C", ()) NOTE(objc_inferring_on_objc_protocol_member,none, @@ -4621,6 +4657,11 @@ ERROR(availability_decl_unavailable, none, "%select{ in %3|}2%select{|: %4}4", (unsigned, DeclName, bool, StringRef, StringRef)) +WARNING(availability_decl_unavailable_warn, none, + "%select{getter for |setter for |}0%1 is unavailable" + "%select{ in %3|}2%select{|: %4}4", + (unsigned, DeclName, bool, StringRef, StringRef)) + #define REPLACEMENT_DECL_KIND_SELECT "select{| instance method| property}" ERROR(availability_decl_unavailable_rename, none, "%select{getter for |setter for |}0%1 has been " @@ -4628,6 +4669,12 @@ ERROR(availability_decl_unavailable_rename, none, "'%4'%select{|: %5}5", (unsigned, DeclName, bool, unsigned, StringRef, StringRef)) +WARNING(availability_decl_unavailable_rename_warn, none, + "%select{getter for |setter for |}0%1 has been " + "%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 " + "'%4'%select{|: %5}5", + (unsigned, DeclName, bool, unsigned, StringRef, StringRef)) + NOTE(availability_marked_unavailable, none, "%select{getter for |setter for |}0%1 has been explicitly marked " "unavailable here", (unsigned, DeclName)) @@ -5122,6 +5169,10 @@ NOTE(function_builder_infer_add_return, none, NOTE(function_builder_infer_pick_specific, none, "apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", (Type, unsigned, DeclName)) +WARNING(function_builder_missing_limited_availability, none, + "function builder %0 does not implement 'buildLimitedAvailability'; " + "this code may crash on earlier versions of the OS", + (Type)) //------------------------------------------------------------------------------ // MARK: Tuple Shuffle Diagnostics diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index e4e0ec2a3bd89..9a4d86d2e9323 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -76,6 +76,9 @@ EDUCATIONAL_NOTES(append_interpolation_static, "string-interpolation-conformance.md") EDUCATIONAL_NOTES(append_interpolation_void_or_discardable, "string-interpolation-conformance.md") -EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md") +EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md") + +EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated, + "trailing-closure-matching.md") #undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index eaca55f951348..a75d9d56b39e4 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -861,9 +861,6 @@ class StringLiteralExpr : public LiteralExpr { /// A UTF-8 string. UTF8, - /// A UTF-16 string. - UTF16, - /// A single UnicodeScalar, passed as an integer. OneUnicodeScalar }; @@ -1074,17 +1071,14 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { class MagicIdentifierLiteralExpr : public LiteralExpr { public: enum Kind : unsigned { - File, FilePath, Line, Column, Function, DSOHandle +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) NAME, +#include "swift/AST/MagicIdentifierKinds.def" }; static StringRef getKindString(MagicIdentifierLiteralExpr::Kind value) { switch (value) { - case File: return "#file"; - case FilePath: return "#filePath"; - case Function: return "#function"; - case Line: return "#line"; - case Column: return "#column"; - case DSOHandle: return "#dsohandle"; +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) case NAME: return STRING; +#include "swift/AST/MagicIdentifierKinds.def" } llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in getKindString."); @@ -1107,21 +1101,15 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { return static_cast(Bits.MagicIdentifierLiteralExpr.Kind); } - bool isFile() const { return getKind() == File; } - bool isFunction() const { return getKind() == Function; } - bool isLine() const { return getKind() == Line; } - bool isColumn() const { return getKind() == Column; } - bool isString() const { switch (getKind()) { - case File: - case FilePath: - case Function: +#define MAGIC_STRING_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case NAME: \ return true; - case Line: - case Column: - case DSOHandle: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case NAME: \ return false; +#include "swift/AST/MagicIdentifierKinds.def" } llvm_unreachable("bad Kind"); } @@ -1973,7 +1961,7 @@ class TryExpr : public AnyTryExpr { /// ForceTryExpr - A 'try!' surrounding an expression, marking that /// the expression contains code which might throw, but that the code /// should dynamically assert if it does. -class ForceTryExpr : public AnyTryExpr { +class ForceTryExpr final : public AnyTryExpr { SourceLoc ExclaimLoc; public: @@ -1992,7 +1980,7 @@ class ForceTryExpr : public AnyTryExpr { /// A 'try?' surrounding an expression, marking that the expression contains /// code which might throw, and that the result should be injected into an /// Optional. If the code does throw, \c nil is produced. -class OptionalTryExpr : public AnyTryExpr { +class OptionalTryExpr final : public AnyTryExpr { SourceLoc QuestionLoc; public: @@ -2011,7 +1999,6 @@ class OptionalTryExpr : public AnyTryExpr { /// An expression node that does not affect the evaluation of its subexpression. class IdentityExpr : public Expr { Expr *SubExpr; - public: IdentityExpr(ExprKind kind, Expr *subExpr, Type ty = Type(), @@ -2102,6 +2089,32 @@ class ParenExpr : public IdentityExpr { static bool classof(const Expr *E) { return E->getKind() == ExprKind::Paren; } }; +/// AwaitExpr - An 'await' surrounding an expression, marking that the +/// expression contains code which is a coroutine that may block. +/// +/// getSemanticsProvidingExpr() looks through this because it doesn't +/// provide the value and only very specific clients care where the +/// 'await' was written. +class AwaitExpr final : public IdentityExpr { + SourceLoc AwaitLoc; +public: + AwaitExpr(SourceLoc awaitLoc, Expr *sub, Type type = Type(), + bool implicit = false) + : IdentityExpr(ExprKind::Await, sub, type, implicit), AwaitLoc(awaitLoc) { + } + + SourceLoc getLoc() const { return AwaitLoc; } + + SourceLoc getAwaitLoc() const { return AwaitLoc; } + SourceLoc getStartLoc() const { return AwaitLoc; } + SourceLoc getEndLoc() const { return getSubExpr()->getEndLoc(); } + + static bool classof(const Expr *e) { + return e->getKind() == ExprKind::Await; + } +}; + + /// TupleExpr - Parenthesized expressions like '(a: x+x)' and '(x, y, 4)'. Also /// used to represent the operands to a binary operator. Note that /// expressions like '(4)' are represented with a ParenExpr. @@ -3700,6 +3713,9 @@ class AbstractClosureExpr : public DeclContext, public Expr { /// Return whether this closure is throwing when fully applied. bool isBodyThrowing() const; + /// \brief Return whether this closure is async when fully applied. + bool isBodyAsync() const; + /// Whether this closure consists of a single expression. bool hasSingleExpressionBody() const; @@ -3768,7 +3784,25 @@ class SerializedAbstractClosureExpr : public SerializedLocalDeclContext { /// { [weak c] (a : Int) -> Int in a + c!.getFoo() } /// \endcode class ClosureExpr : public AbstractClosureExpr { +public: + enum class BodyState { + /// The body was parsed, but not ready for type checking because + /// the closure parameters haven't been type checked. + Parsed, + + /// The type of the closure itself was type checked. But the body has not + /// been type checked yet. + ReadyForTypeChecking, + + /// The body was typechecked with the enclosing closure. + /// i.e. single expression closure or function builder closure. + TypeCheckedWithSignature, + /// The body was type checked separately from the enclosing closure. + SeparatelyTypeChecked, + }; + +private: /// The range of the brackets of the capture list, if present. SourceRange BracketRange; @@ -3792,8 +3826,7 @@ class ClosureExpr : public AbstractClosureExpr { SourceLoc InLoc; /// The explicitly-specified result type. - llvm::PointerIntPair - ExplicitResultTypeAndSeparatelyChecked; + llvm::PointerIntPair ExplicitResultTypeAndBodyState; /// The body of the closure, along with a bit indicating whether it /// was originally just a single expression. @@ -3808,7 +3841,7 @@ class ClosureExpr : public AbstractClosureExpr { BracketRange(bracketRange), CapturedSelfDecl(capturedSelfDecl), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc), InLoc(inLoc), - ExplicitResultTypeAndSeparatelyChecked(explicitResultType, false), + ExplicitResultTypeAndBodyState(explicitResultType, BodyState::Parsed), Body(nullptr) { setParameterList(params); Bits.ClosureExpr.HasAnonymousClosureVars = false; @@ -3863,15 +3896,13 @@ class ClosureExpr : public AbstractClosureExpr { Type getExplicitResultType() const { assert(hasExplicitResultType() && "No explicit result type"); - return ExplicitResultTypeAndSeparatelyChecked.getPointer() - ->getInstanceType(); + return ExplicitResultTypeAndBodyState.getPointer()->getInstanceType(); } void setExplicitResultType(Type ty); TypeRepr *getExplicitResultTypeRepr() const { assert(hasExplicitResultType() && "No explicit result type"); - return ExplicitResultTypeAndSeparatelyChecked.getPointer() - ->getTypeRepr(); + return ExplicitResultTypeAndBodyState.getPointer()->getTypeRepr(); } /// Determine whether the closure has a single expression for its @@ -3913,14 +3944,20 @@ class ClosureExpr : public AbstractClosureExpr { /// captured non-weakly). bool capturesSelfEnablingImplictSelf() const; - /// Whether this closure's body was type checked separately from its - /// enclosing expression. - bool wasSeparatelyTypeChecked() const { - return ExplicitResultTypeAndSeparatelyChecked.getInt(); + + /// Get the type checking state of this closure's body. + BodyState getBodyState() const { + return ExplicitResultTypeAndBodyState.getInt(); + } + void setBodyState(BodyState v) { + ExplicitResultTypeAndBodyState.setInt(v); } - void setSeparatelyTypeChecked(bool flag = true) { - ExplicitResultTypeAndSeparatelyChecked.setInt(flag); + /// Whether this closure's body is/was type checked separately from its + /// enclosing expression. + bool isSeparatelyTypeChecked() const { + return getBodyState() == BodyState::SeparatelyTypeChecked || + getBodyState() == BodyState::ReadyForTypeChecking; } static bool classof(const Expr *E) { @@ -4165,16 +4202,20 @@ class PropertyWrapperValuePlaceholderExpr : public Expr { SourceRange Range; OpaqueValueExpr *Placeholder; Expr *WrappedValue; + bool IsAutoClosure = false; PropertyWrapperValuePlaceholderExpr(SourceRange Range, Type Ty, OpaqueValueExpr *placeholder, - Expr *wrappedValue) + Expr *wrappedValue, + bool isAutoClosure) : Expr(ExprKind::PropertyWrapperValuePlaceholder, /*Implicit=*/true, Ty), - Range(Range), Placeholder(placeholder), WrappedValue(wrappedValue) {} + Range(Range), Placeholder(placeholder), WrappedValue(wrappedValue), + IsAutoClosure(isAutoClosure) {} public: static PropertyWrapperValuePlaceholderExpr * - create(ASTContext &ctx, SourceRange range, Type ty, Expr *wrappedValue); + create(ASTContext &ctx, SourceRange range, Type ty, Expr *wrappedValue, + bool isAutoClosure = false); /// The original wrappedValue initialization expression provided via /// \c = on a proprety with attached property wrappers. @@ -4196,6 +4237,8 @@ class PropertyWrapperValuePlaceholderExpr : public Expr { Placeholder = placeholder; } + bool isAutoClosure() const { return IsAutoClosure; } + SourceRange getSourceRange() const { return Range; } static bool classof(const Expr *E) { @@ -4707,7 +4750,7 @@ class ForcedCheckedCastExpr final : public CheckedCastExpr { /// Represents an explicit conditional checked cast, which converts /// from a type to some subtype and produces an Optional value, which will be -/// .Some(x) if the cast succeeds, or .None if the cast fails. +/// .some(x) if the cast succeeds, or .none if the cast fails. /// Spelled 'a as? T' and produces a value of type 'T?'. class ConditionalCheckedCastExpr final : public CheckedCastExpr { SourceLoc QuestionLoc; @@ -4725,9 +4768,6 @@ class ConditionalCheckedCastExpr final : public CheckedCastExpr { static ConditionalCheckedCastExpr *createImplicit(ASTContext &ctx, Expr *sub, Type castTy); - static ConditionalCheckedCastExpr * - createImplicit(ASTContext &ctx, Expr *sub, TypeRepr *tyRepr, Type castTy); - /// Retrieve the location of the '?' that follows 'as'. SourceLoc getQuestionLoc() const { return QuestionLoc; } @@ -4797,25 +4837,30 @@ class CoerceExpr final : public ExplicitCastExpr { /// may be preceded by the 'throws' keyword. Currently this only exists to be /// transformed into a FunctionTypeRepr by simplifyTypeExpr() in Sema. class ArrowExpr : public Expr { + SourceLoc AsyncLoc; SourceLoc ThrowsLoc; SourceLoc ArrowLoc; Expr *Args; Expr *Result; public: - ArrowExpr(Expr *Args, SourceLoc ThrowsLoc, SourceLoc ArrowLoc, Expr *Result) + ArrowExpr(Expr *Args, SourceLoc AsyncLoc, SourceLoc ThrowsLoc, + SourceLoc ArrowLoc, Expr *Result) : Expr(ExprKind::Arrow, /*implicit=*/false, Type()), - ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), Args(Args), Result(Result) + AsyncLoc(AsyncLoc), ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), Args(Args), + Result(Result) { } - ArrowExpr(SourceLoc ThrowsLoc, SourceLoc ArrowLoc) + ArrowExpr(SourceLoc AsyncLoc, SourceLoc ThrowsLoc, SourceLoc ArrowLoc) : Expr(ExprKind::Arrow, /*implicit=*/false, Type()), - ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), Args(nullptr), Result(nullptr) + AsyncLoc(AsyncLoc), ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), + Args(nullptr), Result(nullptr) { } Expr *getArgsExpr() const { return Args; } void setArgsExpr(Expr *E) { Args = E; } Expr *getResultExpr() const { return Result; } void setResultExpr(Expr *E) { Result = E; } + SourceLoc getAsyncLoc() const { return AsyncLoc; } SourceLoc getThrowsLoc() const { return ThrowsLoc; } SourceLoc getArrowLoc() const { return ArrowLoc; } bool isFolded() const { return Args != nullptr && Result != nullptr; } @@ -4823,6 +4868,7 @@ class ArrowExpr : public Expr { SourceLoc getSourceLoc() const { return ArrowLoc; } SourceLoc getStartLoc() const { return isFolded() ? Args->getStartLoc() : + AsyncLoc.isValid() ? AsyncLoc : ThrowsLoc.isValid() ? ThrowsLoc : ArrowLoc; } SourceLoc getEndLoc() const { @@ -4914,20 +4960,21 @@ class IfExpr : public Expr { /// a particular case. class EnumIsCaseExpr : public Expr { Expr *SubExpr; + TypeRepr *CaseRepr; EnumElementDecl *Element; public: - EnumIsCaseExpr(Expr *SubExpr, EnumElementDecl *Element) - : Expr(ExprKind::EnumIsCase, /*implicit*/ true), - SubExpr(SubExpr), Element(Element) - {} - + EnumIsCaseExpr(Expr *SubExpr, TypeRepr *CaseRepr, EnumElementDecl *Element) + : Expr(ExprKind::EnumIsCase, /*implicit*/ true), SubExpr(SubExpr), + CaseRepr(CaseRepr), Element(Element) {} + Expr *getSubExpr() const { return SubExpr; } void setSubExpr(Expr *e) { SubExpr = e; } - + + TypeRepr *getCaseTypeRepr() const { return CaseRepr; } + EnumElementDecl *getEnumElement() const { return Element; } - void setEnumElement(EnumElementDecl *elt) { Element = elt; } - + SourceLoc getLoc() const { return SubExpr->getLoc(); } SourceLoc getStartLoc() const { return SubExpr->getStartLoc(); } SourceLoc getEndLoc() const { return SubExpr->getEndLoc(); } @@ -5209,6 +5256,7 @@ class KeyPathExpr : public Expr { OptionalWrap, Identity, TupleElement, + DictionaryKey, }; private: @@ -5317,6 +5365,16 @@ class KeyPathExpr : public Expr { propertyType, loc); } + + /// Create a component for a dictionary key (#keyPath only). + static Component forDictionaryKey(DeclNameRef UnresolvedName, + Type valueType, + SourceLoc loc) { + return Component(nullptr, UnresolvedName, nullptr, {}, {}, + Kind::DictionaryKey, + valueType, + loc); + } /// Create a component for a subscript. static Component forSubscript(ASTContext &ctx, @@ -5407,6 +5465,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: return true; case Kind::UnresolvedSubscript: @@ -5431,6 +5490,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: return nullptr; } llvm_unreachable("unhandled kind"); @@ -5450,6 +5510,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: llvm_unreachable("no subscript labels for this kind"); } llvm_unreachable("unhandled kind"); @@ -5472,6 +5533,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: return {}; } llvm_unreachable("unhandled kind"); @@ -5483,6 +5545,7 @@ class KeyPathExpr : public Expr { DeclNameRef getUnresolvedDeclName() const { switch (getKind()) { case Kind::UnresolvedProperty: + case Kind::DictionaryKey: return Decl.UnresolvedName; case Kind::Invalid: @@ -5513,6 +5576,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalForce: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: llvm_unreachable("no decl ref for this kind"); } llvm_unreachable("unhandled kind"); @@ -5532,6 +5596,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::Property: case Kind::Subscript: + case Kind::DictionaryKey: llvm_unreachable("no field number for this kind"); } llvm_unreachable("unhandled kind"); diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index afd5e4f17235b..53ee2da310a42 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -103,7 +103,8 @@ UNCHECKED_EXPR(Sequence, Expr) ABSTRACT_EXPR(Identity, Expr) EXPR(Paren, IdentityExpr) EXPR(DotSelf, IdentityExpr) - EXPR_RANGE(Identity, Paren, DotSelf) + EXPR(Await, IdentityExpr) + EXPR_RANGE(Identity, Paren, Await) ABSTRACT_EXPR(AnyTry, Expr) EXPR(Try, AnyTryExpr) EXPR(ForceTry, AnyTryExpr) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h new file mode 100644 index 0000000000000..639b982d25e73 --- /dev/null +++ b/include/swift/AST/ExtInfo.h @@ -0,0 +1,724 @@ +//===--- ExtInfo.h - Extended information for function types ----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the ASTExtInfo and SILExtInfo classes, which are used to store +// the calling convention and related information for function types in the AST +// and SIL respectively. These types are lightweight and immutable; they are +// constructed using builder-pattern style APIs to enforce invariants. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EXTINFO_H +#define SWIFT_EXTINFO_H + +#include "swift/AST/AutoDiff.h" +#include "swift/AST/ClangModuleLoader.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace clang { +class Type; +} // namespace clang + +namespace swift { +class AnyFunctionType; +class ASTExtInfo; +class ASTExtInfoBuilder; +class FunctionType; +class SILExtInfo; +class SILExtInfoBuilder; +class SILFunctionType; +} // namespace swift + +namespace swift { + +// MARK: - ClangTypeInfo +/// Wrapper class for storing a clang::Type in an (AST|SIL)ExtInfo. +class ClangTypeInfo { + friend AnyFunctionType; + friend FunctionType; + friend SILFunctionType; + friend ASTExtInfoBuilder; + friend SILExtInfoBuilder; + + // We preserve a full clang::Type *, not a clang::FunctionType * as: + // 1. We need to keep sugar in case we need to present an error to the user + // (for AnyFunctionType). + // 2. The actual type being stored is [ignoring sugar] either a + // clang::PointerType, a clang::BlockPointerType, or a + // clang::ReferenceType which points to a clang::FunctionType. + // + // When used as a part of SILFunctionType, the type is canonical. + const clang::Type *type; + + constexpr ClangTypeInfo() : type(nullptr) {} + constexpr ClangTypeInfo(const clang::Type *type) : type(type) {} + + friend bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs); + ClangTypeInfo getCanonical() const; + +public: + constexpr const clang::Type *getType() const { return type; } + + constexpr bool empty() const { return !type; } + + /// Use the ClangModuleLoader to print the Clang type as a string. + void printType(ClangModuleLoader *cml, llvm::raw_ostream &os) const; + + void dump(llvm::raw_ostream &os) const; +}; + +// MARK: - FunctionTypeRepresentation +/// The representation form of a function. +enum class FunctionTypeRepresentation : uint8_t { + /// A "thick" function that carries a context pointer to reference captured + /// state. The default native function representation. + Swift = 0, + + /// A thick function that is represented as an Objective-C block. + Block, + + /// A "thin" function that needs no context. + Thin, + + /// A C function pointer (or reference), which is thin and also uses the C + /// calling convention. + CFunctionPointer, + + /// The value of the greatest AST function representation. + Last = CFunctionPointer, +}; + +// MARK: - SILFunctionTypeRepresentation + +/// The representation form of a SIL function. +/// +/// This is a superset of FunctionTypeRepresentation. The common representations +/// must share an enum value. +/// +/// TODO: The overlap of SILFunctionTypeRepresentation and +/// FunctionTypeRepresentation is a total hack necessitated by the way SIL +/// TypeLowering is currently written. We ought to refactor TypeLowering so that +/// it is not necessary to distinguish these cases. +enum class SILFunctionTypeRepresentation : uint8_t { + /// A freestanding thick function. + Thick = uint8_t(FunctionTypeRepresentation::Swift), + + /// A thick function that is represented as an Objective-C block. + Block = uint8_t(FunctionTypeRepresentation::Block), + + /// A freestanding thin function that needs no context. + Thin = uint8_t(FunctionTypeRepresentation::Thin), + + /// A C function pointer, which is thin and also uses the C calling + /// convention. + CFunctionPointer = uint8_t(FunctionTypeRepresentation::CFunctionPointer), + + /// The value of the greatest AST function representation. + LastAST = CFunctionPointer, + + /// The value of the least SIL-only function representation. + FirstSIL = 8, + + /// A Swift instance method. + Method = FirstSIL, + + /// An Objective-C method. + ObjCMethod, + + /// A Swift protocol witness. + WitnessMethod, + + /// A closure invocation function that has not been bound to a context. + Closure, +}; + +/// Can this calling convention result in a function being called indirectly +/// through the runtime. +constexpr bool canBeCalledIndirectly(SILFunctionTypeRepresentation rep) { + switch (rep) { + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Block: + case SILFunctionTypeRepresentation::Closure: + return false; + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::WitnessMethod: + return true; + } + + llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); +} + +// MARK: - ASTExtInfoBuilder +/// A builder type for creating an \c ASTExtInfo. +/// +/// The main API public includes the \c withXYZ and \p build() methods. +class ASTExtInfoBuilder { + friend AnyFunctionType; + friend ASTExtInfo; + + // If bits are added or removed, then TypeBase::AnyFunctionTypeBits + // and NumMaskBits must be updated, and they must match. + // + // |representation|noEscape|async|throws|differentiability| + // | 0 .. 3 | 4 | 5 | 6 | 7 .. 8 | + // + enum : unsigned { + RepresentationMask = 0xF << 0, + NoEscapeMask = 1 << 4, + AsyncMask = 1 << 5, + ThrowsMask = 1 << 6, + DifferentiabilityMaskOffset = 7, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 9 + }; + + unsigned bits; // Naturally sized for speed. + + ClangTypeInfo clangTypeInfo; + + using Representation = FunctionTypeRepresentation; + + static void assertIsFunctionType(const clang::Type *); + + ASTExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) + : bits(bits), clangTypeInfo(clangTypeInfo) { + // TODO: [clang-function-type-serialization] Once we start serializing + // the Clang type, we should also assert that the pointer is non-null. + auto Rep = Representation(bits & RepresentationMask); + if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) + assertIsFunctionType(clangTypeInfo.type); + } + +public: + // Constructor with all defaults. + ASTExtInfoBuilder() + : ASTExtInfoBuilder(Representation::Swift, false, false, + DifferentiabilityKind::NonDifferentiable, nullptr) {} + + // Constructor for polymorphic type. + ASTExtInfoBuilder(Representation rep, bool throws) + : ASTExtInfoBuilder(rep, false, throws, + DifferentiabilityKind::NonDifferentiable, nullptr) {} + + // Constructor with no defaults. + ASTExtInfoBuilder(Representation rep, bool isNoEscape, bool throws, + DifferentiabilityKind diffKind, const clang::Type *type) + : ASTExtInfoBuilder( + ((unsigned)rep) | (isNoEscape ? NoEscapeMask : 0) | + (throws ? ThrowsMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask), + ClangTypeInfo(type)) {} + + void checkInvariants() const; + + /// Check if \c this is well-formed and create an ExtInfo. + ASTExtInfo build() const; + + constexpr Representation getRepresentation() const { + unsigned rawRep = bits & RepresentationMask; + assert(rawRep <= unsigned(Representation::Last) && + "unexpected SIL representation"); + return Representation(rawRep); + } + + constexpr bool isNoEscape() const { return bits & NoEscapeMask; } + + constexpr bool isAsync() const { return bits & AsyncMask; } + + constexpr bool isThrowing() const { return bits & ThrowsMask; } + + constexpr DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } + + constexpr bool isDifferentiable() const { + return getDifferentiabilityKind() > + DifferentiabilityKind::NonDifferentiable; + } + + /// Get the underlying ClangTypeInfo value if it is not the default value. + Optional getClangTypeInfo() const { + return clangTypeInfo.empty() ? Optional() : clangTypeInfo; + } + + constexpr SILFunctionTypeRepresentation getSILRepresentation() const { + unsigned rawRep = bits & RepresentationMask; + return SILFunctionTypeRepresentation(rawRep); + } + + constexpr bool hasSelfParam() const { + switch (getSILRepresentation()) { + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Block: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Closure: + return false; + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::WitnessMethod: + return true; + } + llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); + } + + /// True if the function representation carries context. + constexpr bool hasContext() const { + switch (getSILRepresentation()) { + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Block: + return true; + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::WitnessMethod: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Closure: + return false; + } + llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); + } + + // Note that we don't have setters. That is by design, use + // the following with methods instead of mutating these objects. + LLVM_NODISCARD + ASTExtInfoBuilder withRepresentation(Representation rep) const { + return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep, + clangTypeInfo); + } + LLVM_NODISCARD + ASTExtInfoBuilder withNoEscape(bool noEscape = true) const { + return ASTExtInfoBuilder(noEscape ? (bits | NoEscapeMask) + : (bits & ~NoEscapeMask), + clangTypeInfo); + } + LLVM_NODISCARD + ASTExtInfoBuilder withAsync(bool async = true) const { + return ASTExtInfoBuilder(async ? (bits | AsyncMask) + : (bits & ~AsyncMask), + clangTypeInfo); + } + LLVM_NODISCARD + ASTExtInfoBuilder withThrows(bool throws = true) const { + return ASTExtInfoBuilder( + throws ? (bits | ThrowsMask) : (bits & ~ThrowsMask), clangTypeInfo); + } + LLVM_NODISCARD + ASTExtInfoBuilder + withDifferentiabilityKind(DifferentiabilityKind differentiability) const { + return ASTExtInfoBuilder( + (bits & ~DifferentiabilityMask) | + ((unsigned)differentiability << DifferentiabilityMaskOffset), + clangTypeInfo); + } + LLVM_NODISCARD + ASTExtInfoBuilder withClangFunctionType(const clang::Type *type) const { + return ASTExtInfoBuilder(bits, ClangTypeInfo(type)); + } + + /// Put a SIL representation in the ExtInfo. + /// + /// SIL type lowering transiently generates AST function types with SIL + /// representations. However, they shouldn't persist in the AST, and + /// don't need to be parsed, printed, or serialized. + LLVM_NODISCARD + ASTExtInfoBuilder + withSILRepresentation(SILFunctionTypeRepresentation rep) const { + return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep, + clangTypeInfo); + } + + bool isEqualTo(ASTExtInfoBuilder other, bool useClangTypes) const { + return bits == other.bits && + (useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true); + } + + constexpr std::pair getFuncAttrKey() const { + return std::make_pair(bits, clangTypeInfo.getType()); + } +}; // end ASTExtInfoBuilder + +// MARK: - ASTExtInfo +/// Calling convention and related information for AnyFunctionType + subclasses. +/// +/// New instances can be made from existing instances via \c ASTExtInfoBuilder, +/// typically using a code pattern like: +/// \code +/// extInfo.intoBuilder().withX(x).withY(y).build() +/// \endcode +class ASTExtInfo { + friend ASTExtInfoBuilder; + friend AnyFunctionType; + + ASTExtInfoBuilder builder; + + ASTExtInfo(ASTExtInfoBuilder builder) : builder(builder) {} + ASTExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo) + : builder(bits, clangTypeInfo){}; + +public: + ASTExtInfo() : builder(){}; + + /// Create a builder with the same state as \c this. + ASTExtInfoBuilder intoBuilder() const { return builder; } + +private: + constexpr unsigned getBits() const { return builder.bits; } + +public: + constexpr FunctionTypeRepresentation getRepresentation() const { + return builder.getRepresentation(); + } + + constexpr SILFunctionTypeRepresentation getSILRepresentation() const { + return builder.getSILRepresentation(); + } + + constexpr bool isNoEscape() const { return builder.isNoEscape(); } + + constexpr bool isAsync() const { return builder.isAsync(); } + + constexpr bool isThrowing() const { return builder.isThrowing(); } + + constexpr DifferentiabilityKind getDifferentiabilityKind() const { + return builder.getDifferentiabilityKind(); + } + + constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } + + Optional getClangTypeInfo() const { + return builder.getClangTypeInfo(); + } + + constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } + + constexpr bool hasContext() const { return builder.hasContext(); } + + /// Helper method for changing the representation. + /// + /// Prefer using \c ASTExtInfoBuilder::withRepresentation for chaining. + LLVM_NODISCARD + ASTExtInfo withRepresentation(ASTExtInfoBuilder::Representation rep) const { + return builder.withRepresentation(rep).build(); + } + + /// Helper method for changing only the noEscape field. + /// + /// Prefer using \c ASTExtInfoBuilder::withNoEscape for chaining. + LLVM_NODISCARD + ASTExtInfo withNoEscape(bool noEscape = true) const { + return builder.withNoEscape(noEscape).build(); + } + + /// Helper method for changing only the throws field. + /// + /// Prefer using \c ASTExtInfoBuilder::withThrows for chaining. + LLVM_NODISCARD + ASTExtInfo withThrows(bool throws = true) const { + return builder.withThrows(throws).build(); + } + + bool isEqualTo(ASTExtInfo other, bool useClangTypes) const { + return builder.isEqualTo(other.builder, useClangTypes); + } + + constexpr std::pair getFuncAttrKey() const { + return builder.getFuncAttrKey(); + } +}; // end ASTExtInfo + +// MARK: - SILFunctionLanguage + +/// A language-level calling convention. +enum class SILFunctionLanguage : uint8_t { + /// A variation of the Swift calling convention. + Swift = 0, + + /// A variation of the C calling convention. + C, +}; + +/// Map a SIL function representation to the base language calling convention +/// it uses. +constexpr +SILFunctionLanguage getSILFunctionLanguage(SILFunctionTypeRepresentation rep) { + switch (rep) { + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Block: + return SILFunctionLanguage::C; + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::WitnessMethod: + case SILFunctionTypeRepresentation::Closure: + return SILFunctionLanguage::Swift; + } + + llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); +} + +// MARK: - SILExtInfoBuilder +/// A builder type for creating an \c SILExtInfo. +/// +/// The main API public includes the \c withXYZ and \p build() methods. +class SILExtInfoBuilder { + friend SILExtInfo; + friend SILFunctionType; + + // If bits are added or removed, then TypeBase::SILFunctionTypeBits + // and NumMaskBits must be updated, and they must match. + + // |representation|pseudogeneric| noescape |differentiability| + // | 0 .. 3 | 4 | 5 | 6 .. 7 | + // + enum : unsigned { + RepresentationMask = 0xF << 0, + PseudogenericMask = 1 << 4, + NoEscapeMask = 1 << 5, + DifferentiabilityMaskOffset = 6, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 8 + }; + + unsigned bits; // Naturally sized for speed. + + ClangTypeInfo clangTypeInfo; + + using Language = SILFunctionLanguage; + using Representation = SILFunctionTypeRepresentation; + + SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) + : bits(bits), clangTypeInfo(clangTypeInfo) {} + +public: + // Constructor with all defaults. + SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} + + // Constructor for polymorphic type. + SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, + DifferentiabilityKind diffKind, const clang::Type *type) + : SILExtInfoBuilder( + ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | + (isNoEscape ? NoEscapeMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask), + ClangTypeInfo(type)) {} + + void checkInvariants() const; + + /// Check if \c this is well-formed and create an ExtInfo. + SILExtInfo build() const; + + /// What is the abstract representation of this function value? + constexpr Representation getRepresentation() const { + return Representation(bits & RepresentationMask); + } + + constexpr Language getLanguage() const { + return getSILFunctionLanguage(getRepresentation()); + } + + /// Is this function pseudo-generic? A pseudo-generic function + /// is not permitted to dynamically depend on its type arguments. + constexpr bool isPseudogeneric() const { return bits & PseudogenericMask; } + + // Is this function guaranteed to be no-escape by the type system? + constexpr bool isNoEscape() const { return bits & NoEscapeMask; } + + constexpr DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } + + constexpr bool isDifferentiable() const { + return getDifferentiabilityKind() != + DifferentiabilityKind::NonDifferentiable; + } + + /// Get the underlying ClangTypeInfo value if it is not the default value. + Optional getClangTypeInfo() const { + return clangTypeInfo.empty() ? Optional() : clangTypeInfo; + } + + constexpr bool hasSelfParam() const { + switch (getRepresentation()) { + case Representation::Thick: + case Representation::Block: + case Representation::Thin: + case Representation::CFunctionPointer: + case Representation::Closure: + return false; + case Representation::ObjCMethod: + case Representation::Method: + case Representation::WitnessMethod: + return true; + } + llvm_unreachable("Unhandled Representation in switch."); + } + + /// True if the function representation carries context. + constexpr bool hasContext() const { + switch (getRepresentation()) { + case Representation::Thick: + case Representation::Block: + return true; + case Representation::Thin: + case Representation::CFunctionPointer: + case Representation::ObjCMethod: + case Representation::Method: + case Representation::WitnessMethod: + case Representation::Closure: + return false; + } + llvm_unreachable("Unhandled Representation in switch."); + } + + // Note that we don't have setters. That is by design, use + // the following with methods instead of mutating these objects. + SILExtInfoBuilder withRepresentation(Representation rep) const { + return SILExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep, + clangTypeInfo); + } + SILExtInfoBuilder withIsPseudogeneric(bool isPseudogeneric = true) const { + return SILExtInfoBuilder(isPseudogeneric ? (bits | PseudogenericMask) + : (bits & ~PseudogenericMask), + clangTypeInfo); + } + SILExtInfoBuilder withNoEscape(bool noEscape = true) const { + return SILExtInfoBuilder(noEscape ? (bits | NoEscapeMask) + : (bits & ~NoEscapeMask), + clangTypeInfo); + } + SILExtInfoBuilder + withDifferentiabilityKind(DifferentiabilityKind differentiability) const { + return SILExtInfoBuilder( + (bits & ~DifferentiabilityMask) | + ((unsigned)differentiability << DifferentiabilityMaskOffset), + clangTypeInfo); + } + + bool isEqualTo(SILExtInfoBuilder other, bool useClangTypes) const { + return bits == other.bits && + (useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true); + } + + constexpr std::pair getFuncAttrKey() const { + return std::make_pair(bits, clangTypeInfo.getType()); + } +}; // end SILExtInfoBuilder + +// MARK: - SILExtInfo +/// Calling convention information for SILFunctionType. +/// +/// New instances can be made from existing instances via \c SILExtInfoBuilder, +/// typically using a code pattern like: +/// \code +/// extInfo.intoBuilder().withX(x).withY(y).build() +/// \endcode +class SILExtInfo { + friend SILExtInfoBuilder; + friend SILFunctionType; + + SILExtInfoBuilder builder; + + SILExtInfo(SILExtInfoBuilder builder) : builder(builder) {} + SILExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo) + : builder(bits, clangTypeInfo){}; + +public: + SILExtInfo() : builder(){}; + + static SILExtInfo getThin() { + return SILExtInfoBuilder(SILExtInfoBuilder::Representation::Thin, false, + false, DifferentiabilityKind::NonDifferentiable, + nullptr) + .build(); + } + + /// Create a builder with the same state as \c this. + SILExtInfoBuilder intoBuilder() const { return builder; } + +private: + constexpr unsigned getBits() const { return builder.bits; } + +public: + constexpr SILFunctionTypeRepresentation getRepresentation() const { + return builder.getRepresentation(); + } + + constexpr SILFunctionLanguage getLanguage() const { + return builder.getLanguage(); + } + + constexpr bool isPseudogeneric() const { return builder.isPseudogeneric(); } + + constexpr bool isNoEscape() const { return builder.isNoEscape(); } + + constexpr DifferentiabilityKind getDifferentiabilityKind() const { + return builder.getDifferentiabilityKind(); + } + + constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } + + Optional getClangTypeInfo() const { + return builder.getClangTypeInfo(); + } + + constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } + + constexpr bool hasContext() const { return builder.hasContext(); } + + /// Helper method for changing the Representation. + /// + /// Prefer using \c SILExtInfoBuilder::withRepresentation for chaining. + SILExtInfo withRepresentation(SILExtInfoBuilder::Representation rep) const { + return builder.withRepresentation(rep).build(); + } + + /// Helper method for changing only the NoEscape field. + /// + /// Prefer using \c SILExtInfoBuilder::withNoEscape for chaining. + SILExtInfo withNoEscape(bool noEscape = true) const { + return builder.withNoEscape(noEscape).build(); + } + + bool isEqualTo(SILExtInfo other, bool useClangTypes) const { + return builder.isEqualTo(other.builder, useClangTypes); + } + + constexpr std::pair getFuncAttrKey() const { + return builder.getFuncAttrKey(); + } +}; + +/// Helper function to obtain the useClangTypes parameter for checking equality +/// of ExtInfos. +/// +/// Typically, the argument will be a function type which was used to obtain one +/// of the ExtInfos. +template bool useClangTypes(HasContext hasContext) { + return hasContext->getASTContext().LangOpts.UseClangFunctionTypes; +} + +} // end namespace swift + +#endif // SWIFT_EXTINFO_H diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index 334c6e5970b07..48d65d4612856 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -95,8 +95,8 @@ class ForeignErrorConvention { CanType ResultType; ForeignErrorConvention(Kind kind, unsigned parameterIndex, IsOwned_t isOwned, - IsReplaced_t isReplaced, Type parameterType, - Type resultType = Type()) + IsReplaced_t isReplaced, CanType parameterType, + CanType resultType = CanType()) : info(kind, parameterIndex, isOwned, isReplaced), ErrorParameterType(parameterType), ResultType(resultType) {} diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 68e13a83baa88..6f840022dba9a 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -124,7 +124,10 @@ class GenericSignature { bool isNull() const { return Ptr == 0; } - const GenericSignatureImpl *operator->() const { return Ptr; } + const GenericSignatureImpl *operator->() const { + assert(Ptr && "Cannot dereference a null GenericSignature!"); + return Ptr; + } explicit operator bool() const { return Ptr != 0; } diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index abb790e466a14..e91543b476539 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -743,12 +743,6 @@ class GenericSignatureBuilder { TypeArrayView genericParams, EquivalenceClass *equivClass); - /// Realize a potential archetype for the given type. - /// - /// The resolved archetype will be written back into the unresolved type, - /// to make the next resolution more efficient. - PotentialArchetype *realizePotentialArchetype(UnresolvedType &type); - public: /// Try to resolve the equivalence class of the given type. /// @@ -757,7 +751,7 @@ class GenericSignatureBuilder { /// \param resolutionKind How to perform the resolution. /// /// \param wantExactPotentialArchetype Whether to return the precise - /// potential archetype described by the type (vs. just the equivalance + /// potential archetype described by the type (vs. just the equivalence /// class and resolved type). ResolvedType maybeResolveEquivalenceClass( Type type, diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index e60873f074638..f805562d7ef52 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -139,6 +139,12 @@ struct PointerAuthOptions : clang::PointerAuthOptions { PointerAuthSchema ResilientClassStubInitCallbacks; }; +enum class JITDebugArtifact : unsigned { + None, ///< None + LLVMIR, ///< LLVM IR + Object, ///< Object File +}; + /// The set of options supported by IR generation. class IRGenOptions { public: @@ -326,6 +332,8 @@ class IRGenOptions { Optional AutolinkRuntimeCompatibilityLibraryVersion; Optional AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion; + JITDebugArtifact DumpJIT = JITDebugArtifact::None; + IRGenOptions() : DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly), Verify(true), OptMode(OptimizationMode::NotSet), diff --git a/include/swift/AST/IRGenRequests.h b/include/swift/AST/IRGenRequests.h index 3887d8f8dca7c..d0684cad12906 100644 --- a/include/swift/AST/IRGenRequests.h +++ b/include/swift/AST/IRGenRequests.h @@ -22,11 +22,14 @@ #include "swift/AST/SimpleRequest.h" #include "swift/Basic/PrimarySpecificPaths.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Target/TargetMachine.h" namespace swift { class SourceFile; class IRGenOptions; class SILModule; +class SILOptions; +struct TBDGenOptions; namespace irgen { class IRGenModule; @@ -38,6 +41,7 @@ namespace llvm { class GlobalVariable; class LLVMContext; class Module; +class TargetMachine; namespace orc { class ThreadSafeModule; @@ -57,8 +61,9 @@ class GeneratedModule final { private: std::unique_ptr Context; std::unique_ptr Module; + std::unique_ptr Target; - GeneratedModule() : Context(nullptr), Module(nullptr) {} + GeneratedModule() : Context(nullptr), Module(nullptr), Target(nullptr) {} GeneratedModule(GeneratedModule const &) = delete; GeneratedModule &operator=(GeneratedModule const &) = delete; @@ -69,10 +74,13 @@ class GeneratedModule final { /// The given pointers must not be null. If a null \c GeneratedModule is /// needed, use \c GeneratedModule::null() instead. explicit GeneratedModule(std::unique_ptr &&Context, - std::unique_ptr &&Module) - : Context(std::move(Context)), Module(std::move(Module)) { + std::unique_ptr &&Module, + std::unique_ptr &&Target) + : Context(std::move(Context)), Module(std::move(Module)), + Target(std::move(Target)) { assert(getModule() && "Use GeneratedModule::null() instead"); assert(getContext() && "Use GeneratedModule::null() instead"); + assert(getTargetMachine() && "Use GeneratedModule::null() instead"); } GeneratedModule(GeneratedModule &&) = default; @@ -96,6 +104,9 @@ class GeneratedModule final { const llvm::LLVMContext *getContext() const { return Context.get(); } llvm::LLVMContext *getContext() { return Context.get(); } + const llvm::TargetMachine *getTargetMachine() const { return Target.get(); } + llvm::TargetMachine *getTargetMachine() { return Target.get(); } + public: /// Release ownership of the context and module to the caller, consuming /// this value in the process. @@ -113,15 +124,23 @@ class GeneratedModule final { }; struct IRGenDescriptor { + llvm::PointerUnion Ctx; + const IRGenOptions &Opts; - llvm::PointerUnion Ctx; + const TBDGenOptions &TBDOpts; + const SILOptions &SILOpts; + + Lowering::TypeConverter &Conv; + + /// The SILModule to emit. If \c nullptr, a fresh SILModule will be requested + /// during IRGen (which will eventually become the default behavior). SILModule *SILMod; + StringRef ModuleName; const PrimarySpecificPaths &PSPs; StringRef PrivateDiscriminator; ArrayRef parallelOutputFilenames; llvm::GlobalVariable **outModuleHash; - llvm::StringSet<> *LinkerDirectives; friend llvm::hash_code hash_value(const IRGenDescriptor &owner) { return llvm::hash_combine(owner.Ctx); @@ -139,38 +158,44 @@ struct IRGenDescriptor { public: static IRGenDescriptor - forFile(const IRGenOptions &Opts, SourceFile &SF, - std::unique_ptr &&SILMod, StringRef ModuleName, - const PrimarySpecificPaths &PSPs, StringRef PrivateDiscriminator, - llvm::GlobalVariable **outModuleHash, - llvm::StringSet<> *LinkerDirectives) { - return IRGenDescriptor{Opts, - &SF, + forFile(FileUnit *file, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, const SILOptions &SILOpts, + Lowering::TypeConverter &Conv, std::unique_ptr &&SILMod, + StringRef ModuleName, const PrimarySpecificPaths &PSPs, + StringRef PrivateDiscriminator, + llvm::GlobalVariable **outModuleHash = nullptr) { + return IRGenDescriptor{file, + Opts, + TBDOpts, + SILOpts, + Conv, SILMod.release(), ModuleName, PSPs, PrivateDiscriminator, {}, - outModuleHash, - LinkerDirectives}; + outModuleHash}; } static IRGenDescriptor - forWholeModule(const IRGenOptions &Opts, swift::ModuleDecl *M, + forWholeModule(ModuleDecl *M, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, const SILOptions &SILOpts, + Lowering::TypeConverter &Conv, std::unique_ptr &&SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, - ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash, - llvm::StringSet<> *LinkerDirectives) { - return IRGenDescriptor{Opts, - M, + ArrayRef parallelOutputFilenames = {}, + llvm::GlobalVariable **outModuleHash = nullptr) { + return IRGenDescriptor{M, + Opts, + TBDOpts, + SILOpts, + Conv, SILMod.release(), ModuleName, PSPs, "", parallelOutputFilenames, - outModuleHash, - LinkerDirectives}; + outModuleHash}; } /// Retrieves the files to perform IR generation for. @@ -179,6 +204,9 @@ struct IRGenDescriptor { /// For a single file, returns its parent module, otherwise returns the module /// itself. ModuleDecl *getParentModule() const; + + /// Compute the linker directives to emit. + std::vector getLinkerDirectives() const; }; /// Report that a request of the given kind is being evaluated, so it @@ -211,6 +239,21 @@ void simple_display(llvm::raw_ostream &out, const IRGenDescriptor &d); SourceLoc extractNearestSourceLoc(const IRGenDescriptor &desc); +/// Returns the optimized IR for a given file or module. Note this runs the +/// entire compiler pipeline and ignores the passed SILModule. +class OptimizedIRRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + GeneratedModule evaluate(Evaluator &evaluator, IRGenDescriptor desc) const; +}; + /// The zone number for IRGen. #define SWIFT_TYPEID_ZONE IRGen #define SWIFT_TYPEID_HEADER "swift/AST/IRGenTypeIDZone.def" diff --git a/include/swift/AST/IRGenTypeIDZone.def b/include/swift/AST/IRGenTypeIDZone.def index e3f2ff3fba08a..d8dbbb770ce88 100644 --- a/include/swift/AST/IRGenTypeIDZone.def +++ b/include/swift/AST/IRGenTypeIDZone.def @@ -17,3 +17,6 @@ SWIFT_REQUEST(IRGen, IRGenRequest, GeneratedModule(IRGenDescriptor), Uncached, NoLocationInfo) +SWIFT_REQUEST(IRGen, OptimizedIRRequest, + GeneratedModule(IRGenDescriptor), + Uncached, NoLocationInfo) diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h index 32e644e78d1e6..bcbc408402214 100644 --- a/include/swift/AST/Initializer.h +++ b/include/swift/AST/Initializer.h @@ -104,7 +104,7 @@ class PatternBindingInitializer : public Initializer { /// If this initializes a single @lazy variable, lazily create a self /// declaration for it to refer to. - ParamDecl *getImplicitSelfDecl(); + ParamDecl *getImplicitSelfDecl() const; static bool classof(const DeclContext *DC) { if (auto init = dyn_cast(DC)) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 24c9f9c3d979c..a9e0f46cb4783 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -38,6 +38,7 @@ IDENTIFIER(buildEither) IDENTIFIER(buildExpression) IDENTIFIER(buildFinalResult) IDENTIFIER(buildIf) +IDENTIFIER(buildLimitedAvailability) IDENTIFIER(buildOptional) IDENTIFIER(callAsFunction) IDENTIFIER(Change) diff --git a/include/swift/AST/LocalizationFormat.h b/include/swift/AST/LocalizationFormat.h index a4205bb85be72..0b0c524eef2d0 100644 --- a/include/swift/AST/LocalizationFormat.h +++ b/include/swift/AST/LocalizationFormat.h @@ -1,5 +1,4 @@ -//===--- LocalizationFormat.h - YAML format for Diagnostic Messages ---*- -// C++ -*-===// +//===--- LocalizationFormat.h - Format for Diagnostic Messages --*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -18,16 +17,125 @@ #ifndef SWIFT_LOCALIZATIONFORMAT_H #define SWIFT_LOCALIZATIONFORMAT_H +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include +#include #include #include +#include +#include namespace swift { enum class DiagID : uint32_t; namespace diag { +using namespace llvm::support; + +class LocalizationWriterInfo { +public: + using key_type = uint32_t; + using key_type_ref = const uint32_t &; + using data_type = std::string; + using data_type_ref = llvm::StringRef; + using hash_value_type = uint32_t; + using offset_type = uint32_t; + + hash_value_type ComputeHash(key_type_ref key) { return llvm::hash_code(key); } + + std::pair EmitKeyDataLength(llvm::raw_ostream &out, + key_type_ref key, + data_type_ref data) { + offset_type dataLength = static_cast(data.size()); + endian::write(out, dataLength, little); + // No need to write the key length; it's constant. + return {sizeof(key_type), dataLength}; + } + + void EmitKey(llvm::raw_ostream &out, key_type_ref key, unsigned len) { + assert(len == sizeof(key_type)); + endian::write(out, key, little); + } + + void EmitData(llvm::raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + out << data; + } +}; + +class LocalizationReaderInfo { +public: + using internal_key_type = uint32_t; + using external_key_type = swift::DiagID; + using data_type = llvm::StringRef; + using hash_value_type = uint32_t; + using offset_type = uint32_t; + + internal_key_type GetInternalKey(external_key_type key) { + return static_cast(key); + } + + external_key_type GetExternalKey(internal_key_type key) { + return static_cast(key); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + hash_value_type ComputeHash(internal_key_type key) { + return llvm::hash_code(key); + } + + static std::pair + ReadKeyDataLength(const unsigned char *&data) { + offset_type dataLength = + endian::readNext(data); + return {sizeof(uint32_t), dataLength}; + } + + internal_key_type ReadKey(const unsigned char *data, offset_type length) { + return endian::readNext(data); + } + + data_type ReadData(internal_key_type Key, const unsigned char *data, + offset_type length) { + return data_type((const char *)data, length); + } +}; + +class SerializedLocalizationWriter { + using offset_type = LocalizationWriterInfo::offset_type; + llvm::OnDiskChainedHashTableGenerator generator; + +public: + /// Enqueue the given diagnostic to be included in a serialized translations + /// file. + /// + /// \param id The identifier associated with the given diagnostic message e.g. + /// 'cannot_convert_argument'. + /// \param translation The localized diagnostic message for the given + /// identifier. + void insert(swift::DiagID id, llvm::StringRef translation); + + /// Write out previously inserted diagnostic translations into the given + /// location. + /// + /// \param filePath The location of the serialized diagnostics file. It's + /// supposed to be a file with '.db' postfix. + /// \returns true if all diagnostic + /// messages have been successfully serialized, false otherwise. + bool emit(llvm::StringRef filePath); +}; + class LocalizationProducer { public: /// If the message isn't available/localized in the current `yaml` file, @@ -41,9 +149,31 @@ class LocalizationProducer { }; class YAMLLocalizationProducer final : public LocalizationProducer { -public: std::vector diagnostics; - explicit YAMLLocalizationProducer(std::string locale, std::string path); + +public: + explicit YAMLLocalizationProducer(llvm::StringRef filePath); + llvm::StringRef getMessageOr(swift::DiagID id, + llvm::StringRef defaultMessage) const override; + + /// Iterate over all of the available (non-empty) translations + /// maintained by this producer, callback gets each translation + /// with its unique identifier. + void forEachAvailable( + llvm::function_ref callback) const; +}; + +class SerializedLocalizationProducer final : public LocalizationProducer { + using SerializedLocalizationTable = + llvm::OnDiskIterableChainedHashTable; + using offset_type = LocalizationReaderInfo::offset_type; + std::unique_ptr Buffer; + std::unique_ptr SerializedTable; + +public: + explicit SerializedLocalizationProducer( + std::unique_ptr buffer); + llvm::StringRef getMessageOr(swift::DiagID id, llvm::StringRef defaultMessage) const override; }; diff --git a/include/swift/AST/MagicIdentifierKinds.def b/include/swift/AST/MagicIdentifierKinds.def new file mode 100644 index 0000000000000..e1ef52edfcaf9 --- /dev/null +++ b/include/swift/AST/MagicIdentifierKinds.def @@ -0,0 +1,116 @@ +//===--- MagicIdentifierKinds.def - Swift #ident metaprogramming -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 defines macros used for macro-metaprogramming with magic +// identifier literals. +// +//===----------------------------------------------------------------------===// + +// Used for any magic identifier. +#ifndef MAGIC_IDENTIFIER +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) +#endif + +// Used for magic identifiers which produce string literals. +#ifndef MAGIC_STRING_IDENTIFIER +#define MAGIC_STRING_IDENTIFIER(NAME, STRING, SYNTAX_KIND) MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) +#endif + +// Used for magic identifiers which produce integer literals. +#ifndef MAGIC_INT_IDENTIFIER +#define MAGIC_INT_IDENTIFIER(NAME, STRING, SYNTAX_KIND) MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) +#endif + +// Used for magic identifiers which produce raw pointers. +#ifndef MAGIC_POINTER_IDENTIFIER +#define MAGIC_POINTER_IDENTIFIER(NAME, STRING, SYNTAX_KIND) MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) +#endif + +// Used when a given token always maps to a particular magic identifier kind. +#ifndef MAGIC_IDENTIFIER_TOKEN +#define MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) +#endif + +// Used when a given token always maps to a particular magic identifier kind, +// but that token is deprecated. +#ifndef MAGIC_IDENTIFIER_DEPRECATED_TOKEN +#define MAGIC_IDENTIFIER_DEPRECATED_TOKEN(NAME, TOKEN) MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) +#endif + + + +// +// Magic string literals +// + +/// The \c #fileID magic identifier literal. +MAGIC_STRING_IDENTIFIER(FileID, "#fileID", PoundFileIDExpr) + MAGIC_IDENTIFIER_TOKEN(FileID, pound_fileID) + +/// The \c #file magic identifier literal, written in code where it is +/// a synonym for \c #fileID (i.e. "Swift 6 mode" code). +MAGIC_STRING_IDENTIFIER(FileIDSpelledAsFile, "#file", PoundFileExpr) + // tok::pound_file is shared with FilePathSpelledAsFile; please write custom + // code paths for it. + +/// The \c #filePath magic identifier literal. +MAGIC_STRING_IDENTIFIER(FilePath, "#filePath", PoundFilePathExpr) + MAGIC_IDENTIFIER_TOKEN(FilePath, pound_filePath) + +/// The \c #file magic identifier literal, written in code where it is +/// a synonym for \c #filePath (i.e. Swift 5 mode code). +MAGIC_STRING_IDENTIFIER(FilePathSpelledAsFile, "#file", PoundFileExpr) + // tok::pound_file is shared with FileIDSpelledAsFile; please write custom + // code paths for it. + MAGIC_IDENTIFIER_DEPRECATED_TOKEN(FilePathSpelledAsFile, kw___FILE__) + +/// The \c #function magic identifier literal. +MAGIC_STRING_IDENTIFIER(Function, "#function", PoundFunctionExpr) + MAGIC_IDENTIFIER_TOKEN(Function, pound_function) + MAGIC_IDENTIFIER_DEPRECATED_TOKEN(Function, kw___FUNCTION__) + + + +// +// Magic integer literals +// + +/// The \c #line magic identifier literal. +MAGIC_INT_IDENTIFIER(Line, "#line", PoundLineExpr) + MAGIC_IDENTIFIER_TOKEN(Line, pound_line) + MAGIC_IDENTIFIER_DEPRECATED_TOKEN(Line, kw___LINE__) + +/// The \c #column magic identifier literal. +MAGIC_INT_IDENTIFIER(Column, "#column", PoundColumnExpr) + MAGIC_IDENTIFIER_TOKEN(Column, pound_column) + MAGIC_IDENTIFIER_DEPRECATED_TOKEN(Column, kw___COLUMN__) + + + +// +// Magic raw pointer literals +// + +/// The \c #dsohandle magic identifier literal. +MAGIC_POINTER_IDENTIFIER(DSOHandle, "#dsohandle", PoundDsohandleExpr) + MAGIC_IDENTIFIER_TOKEN(DSOHandle, pound_dsohandle) + MAGIC_IDENTIFIER_DEPRECATED_TOKEN(DSOHandle, kw___DSO_HANDLE__) + + + + +#undef MAGIC_IDENTIFIER +#undef MAGIC_STRING_IDENTIFIER +#undef MAGIC_INT_IDENTIFIER +#undef MAGIC_POINTER_IDENTIFIER +#undef MAGIC_IDENTIFIER_TOKEN +#undef MAGIC_IDENTIFIER_DEPRECATED_TOKEN diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 3817283cfe3f0..0a1ff8f42ba2a 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -367,26 +367,22 @@ class ModuleDecl : public DeclContext, public TypeDecl { return { Files.begin(), Files.size() }; } - bool isClangModule() const; void addFile(FileUnit &newFile); - /// Creates a map from \c #filePath strings to corresponding \c #file + /// Creates a map from \c #filePath strings to corresponding \c #fileID /// strings, diagnosing any conflicts. /// - /// A given \c #filePath string always maps to exactly one \c #file string, + /// A given \c #filePath string always maps to exactly one \c #fileID string, /// but it is possible for \c #sourceLocation directives to introduce /// duplicates in the opposite direction. If there are such conflicts, this /// method will diagnose the conflict and choose a "winner" among the paths - /// in a reproducible way. The \c bool paired with the \c #file string is + /// in a reproducible way. The \c bool paired with the \c #fileID string is /// \c true for paths which did not have a conflict or won a conflict, and /// \c false for paths which lost a conflict. Thus, if you want to generate a - /// reverse mapping, you should drop or special-case the \c #file strings that - /// are paired with \c false. - /// - /// Note that this returns an empty StringMap if concise \c #file strings are - /// disabled. Users should fall back to using the file path in this case. + /// reverse mapping, you should drop or special-case the \c #fileID strings + /// that are paired with \c false. llvm::StringMap> - computeMagicFileStringMap(bool shouldDiagnose) const; + computeFileIDMap(bool shouldDiagnose) const; /// Add a file declaring a cross-import overlay. void addCrossImportOverlayFile(StringRef file); diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index e948157c832ac..44cceb47af7ba 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -35,6 +35,29 @@ class Identifier; /// Which kind of module dependencies we are looking for. enum class ModuleDependenciesKind : int8_t { Swift, + // Placeholder dependencies are a kind of dependencies used only by the + // dependency scanner. They are swift modules that the scanner will not be + // able to locate in its search paths and which are the responsibility of the + // scanner's client to ensure are provided. + // + // Placeholder dependencies will be specified in the scanner's output + // dependency graph where it is the responsibility of the scanner's client to + // ensure required post-processing takes place to "resolve" them. In order to + // do so, the client (swift driver, or any other client build system) is + // expected to have access to a full dependency graph of all placeholder + // dependencies and be able to replace placeholder nodes in the dependency + // graph with their full dependency trees, `uniquing` common dependency module + // nodes in the process. + // + // One example where placeholder dependencies are employed is when using + // SwiftPM in Explicit Module Build mode. SwiftPM constructs a build plan for + // all targets ahead-of-time. When planning a build for a target that depends + // on other targets, the dependency scanning action is not able to locate + // dependency target modules, because they have not yet been built. Instead, + // the build system treats them as placeholder dependencies and resolves them + // with `actual` dependencies in a post-processing step once dependency graphs + // of all targets, individually, have been computed. + SwiftPlaceholder, Clang, }; @@ -43,11 +66,11 @@ enum class ModuleDependenciesKind : int8_t { /// This class is mostly an implementation detail for \c ModuleDependencies. class ModuleDependenciesStorageBase { public: - const bool isSwiftModule; + const ModuleDependenciesKind dependencyKind; - ModuleDependenciesStorageBase(bool isSwiftModule, + ModuleDependenciesStorageBase(ModuleDependenciesKind dependencyKind, const std::string &compiledModulePath) - : isSwiftModule(isSwiftModule), + : dependencyKind(dependencyKind), compiledModulePath(compiledModulePath) { } virtual ModuleDependenciesStorageBase *clone() const = 0; @@ -69,6 +92,9 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { /// The Swift interface file, if it can be used to generate the module file. const Optional swiftInterfaceFile; + /// Potentially ready-to-use compiled modules for the interface file. + const std::vector compiledModuleCandidates; + /// The Swift frontend invocation arguments to build the Swift module from the /// interface. const std::vector buildCommandLine; @@ -96,11 +122,15 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { SwiftModuleDependenciesStorage( const std::string &compiledModulePath, const Optional &swiftInterfaceFile, + ArrayRef compiledModuleCandidates, ArrayRef buildCommandLine, ArrayRef extraPCMArgs, StringRef contextHash - ) : ModuleDependenciesStorageBase(/*isSwiftModule=*/true, compiledModulePath), + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Swift, + compiledModulePath), swiftInterfaceFile(swiftInterfaceFile), + compiledModuleCandidates(compiledModuleCandidates.begin(), + compiledModuleCandidates.end()), buildCommandLine(buildCommandLine.begin(), buildCommandLine.end()), extraPCMArgs(extraPCMArgs.begin(), extraPCMArgs.end()), contextHash(contextHash) { } @@ -110,7 +140,7 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { } static bool classof(const ModuleDependenciesStorageBase *base) { - return base->isSwiftModule; + return base->dependencyKind == ModuleDependenciesKind::Swift; } }; @@ -137,7 +167,7 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies - ) : ModuleDependenciesStorageBase(/*isSwiftModule=*/false, + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Clang, compiledModulePath), moduleMapFile(moduleMapFile), contextHash(contextHash), @@ -149,7 +179,35 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { } static bool classof(const ModuleDependenciesStorageBase *base) { - return !base->isSwiftModule; + return base->dependencyKind == ModuleDependenciesKind::Clang; + } +}; + +/// Describes an placeholder Swift module dependency module stub. +/// +/// This class is mostly an implementation detail for \c ModuleDependencies. +class PlaceholderSwiftModuleDependencyStorage : public ModuleDependenciesStorageBase { +public: + PlaceholderSwiftModuleDependencyStorage(const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath) + : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftPlaceholder, + compiledModulePath), + moduleDocPath(moduleDocPath), + sourceInfoPath(sourceInfoPath) {} + + ModuleDependenciesStorageBase *clone() const override { + return new PlaceholderSwiftModuleDependencyStorage(*this); + } + + /// The path to the .swiftModuleDoc file. + const std::string moduleDocPath; + + /// The path to the .swiftSourceInfo file. + const std::string sourceInfoPath; + + static bool classof(const ModuleDependenciesStorageBase *base) { + return base->dependencyKind == ModuleDependenciesKind::SwiftPlaceholder; } }; @@ -181,13 +239,14 @@ class ModuleDependencies { /// built from a Swift interface file (\c .swiftinterface). static ModuleDependencies forSwiftInterface( const std::string &swiftInterfaceFile, + ArrayRef compiledCandidates, ArrayRef buildCommands, ArrayRef extraPCMArgs, StringRef contextHash) { std::string compiledModulePath; return ModuleDependencies( std::make_unique( - compiledModulePath, swiftInterfaceFile, buildCommands, + compiledModulePath, swiftInterfaceFile, compiledCandidates, buildCommands, extraPCMArgs, contextHash)); } @@ -196,7 +255,7 @@ class ModuleDependencies { const std::string &compiledModulePath) { return ModuleDependencies( std::make_unique( - compiledModulePath, None, ArrayRef(), + compiledModulePath, None, ArrayRef(), ArrayRef(), ArrayRef(), StringRef())); } @@ -205,8 +264,8 @@ class ModuleDependencies { std::string compiledModulePath; return ModuleDependencies( std::make_unique( - compiledModulePath, None, ArrayRef(), extraPCMArgs, - StringRef())); + compiledModulePath, None, ArrayRef(), + ArrayRef(), extraPCMArgs, StringRef())); } /// Describe the module dependencies for a Clang module that can be @@ -223,6 +282,16 @@ class ModuleDependencies { fileDependencies)); } + /// Describe a placeholder dependency swift module. + static ModuleDependencies forPlaceholderSwiftModuleStub( + const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath) { + return ModuleDependencies( + std::make_unique( + compiledModulePath, moduleDocPath, sourceInfoPath)); + } + /// Retrieve the path to the compiled module. const std::string getCompiledModulePath() const { return storage->compiledModulePath; @@ -236,9 +305,11 @@ class ModuleDependencies { /// Whether the dependencies are for a Swift module. bool isSwiftModule() const; + /// Whether this represents a placeholder module stub + bool isPlaceholderSwiftModule() const; + ModuleDependenciesKind getKind() const { - return isSwiftModule() ? ModuleDependenciesKind::Swift - : ModuleDependenciesKind::Clang; + return storage->dependencyKind; } /// Retrieve the dependencies for a Swift module. const SwiftModuleDependenciesStorage *getAsSwiftModule() const; @@ -246,9 +317,13 @@ class ModuleDependencies { /// Retrieve the dependencies for a Clang module. const ClangModuleDependenciesStorage *getAsClangModule() const; + /// Retrieve the dependencies for a placeholder dependency module stub. + const PlaceholderSwiftModuleDependencyStorage * + getAsPlaceholderDependencyModule() const; + /// Add a dependency on the given module, if it was not already in the set. void addModuleDependency(StringRef module, - llvm::StringSet<> &alreadyAddedModules); + llvm::StringSet<> *alreadyAddedModules = nullptr); /// Add all of the module dependencies for the imports in the given source /// file to the set of module dependencies. @@ -286,6 +361,9 @@ class ModuleDependenciesCache { /// Dependencies for Swift modules that have already been computed. llvm::StringMap SwiftModuleDependencies; + /// Dependencies for Swift placeholder dependency modules. + llvm::StringMap PlaceholderSwiftModuleDependencies; + /// Dependencies for Clang modules that have already been computed. llvm::StringMap ClangModuleDependencies; diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index a1c91e205a981..1460daf9f44c1 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -107,7 +107,7 @@ struct InterfaceSubContextDelegate { StringRef interfacePath, StringRef outputPath, SourceLoc diagLoc, - llvm::function_ref, + llvm::function_ref, ArrayRef, StringRef)> action) = 0; virtual bool runInSubCompilerInstance(StringRef moduleName, StringRef interfacePath, diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 9d6a68516c494..57e6c63740d5c 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -512,6 +512,14 @@ void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name, } // end namespace namelookup +/// Retrieve the set of nominal type declarations that are directly +/// referenced in the given \c typeRepr, looking through typealiases. +/// +/// \param dc The \c DeclContext from which to perform lookup. +TinyPtrVector +getDirectlyReferencedNominalTypeDecls(ASTContext &ctx, TypeRepr *typeRepr, + DeclContext *dc, bool &anyObject); + /// Retrieve the set of nominal type declarations that are directly /// "inherited" by the given declaration at a particular position in the /// list of "inherited" types. @@ -519,9 +527,8 @@ void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name, /// Add anything we find to the \c result vector. If we come across the /// AnyObject type, set \c anyObject true. void getDirectlyInheritedNominalTypeDecls( - llvm::PointerUnion decl, - unsigned i, - llvm::SmallVectorImpl> &result, + llvm::PointerUnion decl, + unsigned i, llvm::SmallVectorImpl> &result, bool &anyObject); /// Retrieve the set of nominal type declarations that are directly @@ -529,26 +536,25 @@ void getDirectlyInheritedNominalTypeDecls( /// and splitting out the components of compositions. /// /// If we come across the AnyObject type, set \c anyObject true. -SmallVector, 4> -getDirectlyInheritedNominalTypeDecls( - llvm::PointerUnion decl, - bool &anyObject); +SmallVector, 4> getDirectlyInheritedNominalTypeDecls( + llvm::PointerUnion decl, + bool &anyObject); /// Retrieve the set of nominal type declarations that appear as the /// constraint type of any "Self" constraints in the where clause of the /// given protocol or protocol extension. SelfBounds getSelfBoundsFromWhereClause( - llvm::PointerUnion decl); + llvm::PointerUnion decl); /// Retrieve the TypeLoc at the given \c index from among the set of /// type declarations that are directly "inherited" by the given declaration. -inline TypeLoc & -getInheritedTypeLocAtIndex(llvm::PointerUnion decl, - unsigned index) { - if (auto typeDecl = decl.dyn_cast()) +inline const TypeLoc &getInheritedTypeLocAtIndex( + llvm::PointerUnion decl, + unsigned index) { + if (auto typeDecl = decl.dyn_cast()) return typeDecl->getInherited()[index]; - return decl.get()->getInherited()[index]; + return decl.get()->getInherited()[index]; } namespace namelookup { @@ -700,6 +706,31 @@ class ASTScope { computeIsCascadingUse(ArrayRef history, Optional initialIsCascadingUse); + /// Entry point to record the visible statement labels from the given + /// point. + /// + /// This lookup only considers labels that are visible within the current + /// function, so it will not return any labels from lexical scopes that + /// are not reachable via labeled control flow. + /// + /// \returns the set of labeled statements visible from the given source + /// location, with the innermost labeled statement first and proceeding + /// to the outermost labeled statement. + static llvm::SmallVector + lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc); + + /// Look for the directly enclosing case statement and the next case + /// statement, which together act as the source and destination for a + /// 'fallthrough' statement within a switch case. + /// + /// \returns a pair (fallthrough source, fallthrough dest). If the location + /// is not within the body of a case statement at all, the fallthrough + /// source will be \c nullptr. If there is a fallthrough source that case is + /// the last one, the fallthrough destination will be \c nullptr. A + /// well-formed 'fallthrough' statement has both a source and destination. + static std::pair + lookupFallthroughSourceAndDest(SourceFile *sourceFile, SourceLoc loc); + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &) const; void dumpOneScopeMapLocation(std::pair); diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index be6be839b0a81..b1433f82767dc 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -51,8 +51,8 @@ enum class ResolutionKind; /// Display a nominal type or extension thereof. void simple_display( - llvm::raw_ostream &out, - const llvm::PointerUnion &value); + llvm::raw_ostream &out, + const llvm::PointerUnion &value); /// Describes a set of type declarations that are "direct" referenced by /// a particular type in the AST. @@ -76,12 +76,13 @@ using DirectlyReferencedTypeDecls = llvm::TinyPtrVector; /// /// The inherited declaration of \c D at index 0 is the class declaration C. /// The inherited declaration of \c D at index 1 is the typealias Alias. -class InheritedDeclsReferencedRequest : - public SimpleRequest, - unsigned), - RequestFlags::Uncached> // FIXME: Cache these +class InheritedDeclsReferencedRequest + : public SimpleRequest< + InheritedDeclsReferencedRequest, + DirectlyReferencedTypeDecls( + llvm::PointerUnion, + unsigned), + RequestFlags::Uncached> // FIXME: Cache these { public: using SimpleRequest::SimpleRequest; @@ -90,10 +91,10 @@ class InheritedDeclsReferencedRequest : friend SimpleRequest; // Evaluation. - DirectlyReferencedTypeDecls evaluate( - Evaluator &evaluator, - llvm::PointerUnion decl, - unsigned index) const; + DirectlyReferencedTypeDecls + evaluate(Evaluator &evaluator, + llvm::PointerUnion decl, + unsigned index) const; public: // Caching @@ -251,11 +252,11 @@ struct SelfBounds { /// Request the nominal types that occur as the right-hand side of "Self: Foo" /// constraints in the "where" clause of a protocol extension. -class SelfBoundsFromWhereClauseRequest : - public SimpleRequest), - RequestFlags::Uncached> { +class SelfBoundsFromWhereClauseRequest + : public SimpleRequest), + RequestFlags::Uncached> { public: using SimpleRequest::SimpleRequest; @@ -263,11 +264,11 @@ class SelfBoundsFromWhereClauseRequest : friend SimpleRequest; // Evaluation. - SelfBounds evaluate(Evaluator &evaluator, - llvm::PointerUnion) const; + SelfBounds + evaluate(Evaluator &evaluator, + llvm::PointerUnion) const; }; - /// Request all type aliases and nominal types that appear in the "where" /// clause of an extension. class TypeDeclsFromWhereClauseRequest : diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index b67a2bbabe078..4f9028d692392 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -45,7 +45,8 @@ SWIFT_REQUEST(NameLookup, GetDestructorRequest, DestructorDecl *(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(NameLookup, InheritedDeclsReferencedRequest, DirectlyReferencedTypeDecls( - llvm::PointerUnion, unsigned), + llvm::PointerUnion, unsigned), Uncached, HasNearestLocation) SWIFT_REQUEST(NameLookup, InheritedProtocolsRequest, ArrayRef(ProtocolDecl *), SeparatelyCached, @@ -68,7 +69,8 @@ SWIFT_REQUEST(NameLookup, QualifiedLookupRequest, DeclNameRef, NLOptions), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest, - SelfBounds(llvm::PointerUnion), + SelfBounds(llvm::PointerUnion), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SuperclassDeclRequest, ClassDecl *(NominalTypeDecl *), SeparatelyCached, NoLocationInfo) diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index a5005df8d7ddf..6424dd12a72f2 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -34,7 +34,6 @@ namespace swift { class Expr; enum class CheckedCastKind : unsigned; class TypeExpr; - class TypeLoc; /// PatternKind - The classification of different kinds of /// value-matching pattern. @@ -73,7 +72,7 @@ class alignas(8) Pattern { Value : 1 ); - SWIFT_INLINE_BITFIELD(VarPattern, Pattern, 1, + SWIFT_INLINE_BITFIELD(BindingPattern, Pattern, 1, /// True if this is a let pattern, false if a var pattern. IsLet : 1 ); @@ -113,7 +112,7 @@ class alignas(8) Pattern { /// Find the smallest subpattern which obeys the property that matching it is /// equivalent to matching this pattern. /// - /// Looks through ParenPattern, VarPattern, and TypedPattern. + /// Looks through ParenPattern, BindingPattern, and TypedPattern. Pattern *getSemanticsProvidingPattern(); const Pattern *getSemanticsProvidingPattern() const { return const_cast(this)->getSemanticsProvidingPattern(); @@ -447,7 +446,6 @@ class TypedPattern : public Pattern { TypeRepr *getTypeRepr() const { return PatTypeRepr; } - TypeLoc getTypeLoc() const; SourceLoc getLoc() const; SourceRange getSourceRange() const; @@ -692,22 +690,23 @@ class ExprPattern : public Pattern { /// semantics of its own, but has a syntactic effect on the subpattern. Bare /// identifiers in the subpattern create new variable bindings instead of being /// parsed as expressions referencing existing entities. -class VarPattern : public Pattern { +class BindingPattern : public Pattern { SourceLoc VarLoc; Pattern *SubPattern; public: - VarPattern(SourceLoc loc, bool isLet, Pattern *sub) - : Pattern(PatternKind::Var), VarLoc(loc), SubPattern(sub) { - Bits.VarPattern.IsLet = isLet; + BindingPattern(SourceLoc loc, bool isLet, Pattern *sub) + : Pattern(PatternKind::Binding), VarLoc(loc), SubPattern(sub) { + Bits.BindingPattern.IsLet = isLet; } - static VarPattern *createImplicit(ASTContext &Ctx, bool isLet, Pattern *sub) { - auto *VP = new (Ctx) VarPattern(SourceLoc(), isLet, sub); + static BindingPattern *createImplicit(ASTContext &Ctx, bool isLet, + Pattern *sub) { + auto *VP = new (Ctx) BindingPattern(SourceLoc(), isLet, sub); VP->setImplicit(); return VP; } - bool isLet() const { return Bits.VarPattern.IsLet; } + bool isLet() const { return Bits.BindingPattern.IsLet; } SourceLoc getLoc() const { return VarLoc; } SourceRange getSourceRange() const { @@ -722,17 +721,16 @@ class VarPattern : public Pattern { void setSubPattern(Pattern *p) { SubPattern = p; } static bool classof(const Pattern *P) { - return P->getKind() == PatternKind::Var; + return P->getKind() == PatternKind::Binding; } }; - inline Pattern *Pattern::getSemanticsProvidingPattern() { if (auto *pp = dyn_cast(this)) return pp->getSubPattern()->getSemanticsProvidingPattern(); if (auto *tp = dyn_cast(this)) return tp->getSubPattern()->getSemanticsProvidingPattern(); - if (auto *vp = dyn_cast(this)) + if (auto *vp = dyn_cast(this)) return vp->getSubPattern()->getSemanticsProvidingPattern(); return this; } diff --git a/include/swift/AST/PatternNodes.def b/include/swift/AST/PatternNodes.def index 2a73a3735165a..29cd6070657db 100644 --- a/include/swift/AST/PatternNodes.def +++ b/include/swift/AST/PatternNodes.def @@ -32,13 +32,13 @@ #define LAST_PATTERN(Id) #endif -// Metavars: x (variable), pat (pattern), e (expression) -PATTERN(Paren, Pattern) // (pat) -PATTERN(Tuple, Pattern) // (pat1, ..., patN), N >= 1 -PATTERN(Named, Pattern) // let pat, var pat -PATTERN(Any, Pattern) // _ -PATTERN(Typed, Pattern) // pat : type -PATTERN(Var, Pattern) // x +// Metavars: x (variable binding), pat (pattern), e (expression) +PATTERN(Paren, Pattern) // (pat) +PATTERN(Tuple, Pattern) // (pat1, ..., patN), N >= 1 +PATTERN(Named, Pattern) // let pat, var pat +PATTERN(Any, Pattern) // _ +PATTERN(Typed, Pattern) // pat : type +PATTERN(Binding, Pattern) // x REFUTABLE_PATTERN(Is, Pattern) // x is myclass REFUTABLE_PATTERN(EnumElement, Pattern) // .mycase(pat1, ..., patN) // MyType.mycase(pat1, ..., patN) diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 8fdfac5488e61..38611e1245b56 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -327,8 +327,7 @@ struct PrintOptions { }; /// Whether to print function @convention attribute on function types. - // FIXME: [clang-function-type-serialization] Once we start serializing Clang - // types, we should also start printing the full type in the swiftinterface. + // [TODO: Clang-type-plumbing] Print the full type in the swiftinterface. FunctionRepresentationMode PrintFunctionRepresentationAttrs = FunctionRepresentationMode::NameOnly; diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 5ad7db49475ae..f1a1b57490234 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -43,10 +43,6 @@ struct PropertyWrapperTypeInfo { HasInitialValueInit } wrappedValueInit = NoWrappedValueInit; - /// Whether the init(wrappedValue:), if it exists, has the wrappedValue - /// argument as an escaping autoclosure. - bool isWrappedValueInitUsingEscapingAutoClosure = false; - /// The initializer that will be called to default-initialize a /// value with an attached property wrapper. enum { @@ -153,17 +149,6 @@ struct PropertyWrapperBackingPropertyInfo { /// '$foo' from `backingVar`. VarDecl *storageWrapperVar = nullptr; - /// When the original default value is specified in terms of an '=' - /// initializer on the initial property, e.g., - /// - /// \code - /// @Lazy var i = 17 - /// \endcode - /// - /// This is the specified initial value (\c 17), which is suitable for - /// embedding in the expression \c initializeFromOriginal. - Expr *originalInitialValue = nullptr; - /// An expression that initializes the backing property from a value of /// the original property's type (e.g., via `init(wrappedValue:)`), or /// \c NULL if the backing property can only be initialized directly. @@ -171,19 +156,17 @@ struct PropertyWrapperBackingPropertyInfo { /// When \c initializeFromOriginal is non-NULL, the opaque value that /// is used as a stand-in for a value of the original property's type. - OpaqueValueExpr *underlyingValue = nullptr; + PropertyWrapperValuePlaceholderExpr *wrappedValuePlaceholder = nullptr; PropertyWrapperBackingPropertyInfo() { } PropertyWrapperBackingPropertyInfo(VarDecl *backingVar, - VarDecl *storageWrapperVar, - Expr *originalInitialValue, - Expr *initializeFromOriginal, - OpaqueValueExpr *underlyingValue) + VarDecl *storageWrapperVar, + Expr *initializeFromOriginal, + PropertyWrapperValuePlaceholderExpr *placeholder) : backingVar(backingVar), storageWrapperVar(storageWrapperVar), - originalInitialValue(originalInitialValue), initializeFromOriginal(initializeFromOriginal), - underlyingValue(underlyingValue) { } + wrappedValuePlaceholder(placeholder) { } /// Whether this is a valid property wrapper. bool isValid() const { diff --git a/include/swift/AST/SILOptimizerRequests.h b/include/swift/AST/SILOptimizerRequests.h index a4d89efc971a3..a48c794a07254 100644 --- a/include/swift/AST/SILOptimizerRequests.h +++ b/include/swift/AST/SILOptimizerRequests.h @@ -19,6 +19,7 @@ #include "swift/AST/ASTTypeIDs.h" #include "swift/AST/EvaluatorDependencies.h" +#include "swift/AST/SILGenRequests.h" #include "swift/AST/SimpleRequest.h" namespace swift { @@ -69,6 +70,23 @@ void simple_display(llvm::raw_ostream &out, SourceLoc extractNearestSourceLoc(const SILPipelineExecutionDescriptor &desc); +/// Produces lowered SIL from a Swift file or module, ready for IRGen. This runs +/// the diagnostic, optimization, and lowering SIL passes. +class LoweredSILRequest + : public SimpleRequest(ASTLoweringDescriptor), + RequestFlags::Uncached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + std::unique_ptr evaluate(Evaluator &evaluator, + ASTLoweringDescriptor desc) const; +}; + /// Report that a request of the given kind is being evaluated, so it /// can be recorded by the stats reporter. template diff --git a/include/swift/AST/SILOptimizerTypeIDZone.def b/include/swift/AST/SILOptimizerTypeIDZone.def index 55e5d4d3cfc85..9b3d502178e6f 100644 --- a/include/swift/AST/SILOptimizerTypeIDZone.def +++ b/include/swift/AST/SILOptimizerTypeIDZone.def @@ -17,3 +17,6 @@ SWIFT_REQUEST(SILOptimizer, ExecuteSILPipelineRequest, evaluator::SideEffect(SILPipelineExecutionDescriptor), Uncached, NoLocationInfo) +SWIFT_REQUEST(SILOptimizer, LoweredSILRequest, + std::unique_ptr(ASTLoweringDescriptor), + Uncached, NoLocationInfo) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index cd65528e48f17..08757ffd796de 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -85,8 +85,14 @@ class SearchPathOptions { /// The paths to a set of explicitly built modules from interfaces. std::vector ExplicitSwiftModules; + /// A set of compiled modules that may be ready to use. + std::vector CandidateCompiledModules; + /// A map of explict Swift module information. std::string ExplicitSwiftModuleMap; + + /// A map of placeholder Swift module dependency information. + std::string PlaceholderDependencyModuleMap; private: static StringRef pathStringFromFrameworkSearchPath(const FrameworkSearchPath &next) { diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index a77a6b295ad24..ddb5a42e34c1f 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -30,6 +30,7 @@ SEMANTICS_ATTR(STRING_ESCAPE_PERCENT_GET, "string.escapePercent.get") SEMANTICS_ATTR(STRING_CONCAT, "string.concat") SEMANTICS_ATTR(STRING_APPEND, "string.append") SEMANTICS_ATTR(STRING_INIT_EMPTY, "string.init_empty") +SEMANTICS_ATTR(STRING_INIT_EMPTY_WITH_CAPACITY, "string.init_empty_with_capacity") SEMANTICS_ATTR(STRING_PLUS_EQUALS, "string.plusequals") SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE, "findStringSwitchCase") SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE_WITH_CACHE, "findStringSwitchCaseWithCache") @@ -63,6 +64,7 @@ SEMANTICS_ATTR(ARRAY_UNINITIALIZED_INTRINSIC, "array.uninitialized_intrinsic") SEMANTICS_ATTR(ARRAY_FINALIZE_INTRINSIC, "array.finalize_intrinsic") SEMANTICS_ATTR(SEQUENCE_FOR_EACH, "sequence.forEach") +SEMANTICS_ATTR(TYPENAME, "typeName") SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER, "optimize.sil.specialize.generic.never") SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER, @@ -93,5 +95,16 @@ SEMANTICS_ATTR(CONVERT_TO_OBJECTIVE_C, "convertToObjectiveC") SEMANTICS_ATTR(KEYPATH_KVC_KEY_PATH_STRING, "keypath.kvcKeyPathString") +/// The prefix used to force opt-remarks to be emitted in a specific function. +/// +/// If used just by itself "optremark", it is assumed that /all/ opt remarks +/// should be emitted. Otherwise, one can add a suffix after a '.' that +/// specifies a pass to emit opt-remarks from. So for instance to get just +/// information from 'sil-opt-remark-gen', one would write: +/// "optremark.sil-opt-remark-gen". One can add as many as one wishes. Keep in +/// mind that if the function itself is inlined, one will lose the optremark so +/// consider inlining where to put these. +SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark") + #undef SEMANTICS_ATTR diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 7c8e535efbb2d..258ebe5e7e4d6 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -814,13 +814,13 @@ class CaseLabelItem { Default, }; - Pattern *CasePattern; + llvm::PointerIntPair CasePatternAndResolved; SourceLoc WhereLoc; llvm::PointerIntPair GuardExprAndKind; CaseLabelItem(Kind kind, Pattern *casePattern, SourceLoc whereLoc, Expr *guardExpr) - : CasePattern(casePattern), WhereLoc(whereLoc), + : CasePatternAndResolved(casePattern, false), WhereLoc(whereLoc), GuardExprAndKind(guardExpr, kind) {} public: @@ -848,9 +848,19 @@ class CaseLabelItem { SourceLoc getEndLoc() const; SourceRange getSourceRange() const; - Pattern *getPattern() { return CasePattern; } - const Pattern *getPattern() const { return CasePattern; } - void setPattern(Pattern *CasePattern) { this->CasePattern = CasePattern; } + Pattern *getPattern() { + return CasePatternAndResolved.getPointer(); + } + const Pattern *getPattern() const { + return CasePatternAndResolved.getPointer(); + } + bool isPatternResolved() const { + return CasePatternAndResolved.getInt(); + } + void setPattern(Pattern *CasePattern, bool resolved) { + this->CasePatternAndResolved.setPointer(CasePattern); + this->CasePatternAndResolved.setInt(resolved); + } /// Return the guard expression if present, or null if the case label has /// no guard. @@ -1073,6 +1083,10 @@ class CaseStmt final return *CaseBodyVariables; } + /// Find the next case statement within the same 'switch' or 'do-catch', + /// if there is one. + CaseStmt *findNextCaseStmt() const; + static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; } size_t numTrailingObjects(OverloadToken) const { @@ -1353,6 +1367,13 @@ class PoundAssertStmt : public Stmt { } }; +inline void simple_display(llvm::raw_ostream &out, Stmt *S) { + if (S) + out << Stmt::getKindName(S->getKind()); + else + out << "(null)"; +}; + } // end namespace swift #endif // SWIFT_AST_STMT_H diff --git a/include/swift/AST/TBDGenRequests.h b/include/swift/AST/TBDGenRequests.h index d6e781c20b639..66ac0ef814e04 100644 --- a/include/swift/AST/TBDGenRequests.h +++ b/include/swift/AST/TBDGenRequests.h @@ -21,6 +21,10 @@ #include "swift/AST/SimpleRequest.h" namespace llvm { + +class DataLayout; +class Triple; + namespace MachO { class InterfaceFile; } // end namespace MachO @@ -59,6 +63,9 @@ class TBDGenDescriptor final { /// Returns the TBDGen options. const TBDGenOptions &getOptions() const { return Opts; } + const llvm::DataLayout &getDataLayout() const; + const llvm::Triple &getTarget() const; + bool operator==(const TBDGenDescriptor &other) const; bool operator!=(const TBDGenDescriptor &other) const { return !(*this == other); @@ -77,13 +84,27 @@ llvm::hash_code hash_value(const TBDGenDescriptor &desc); void simple_display(llvm::raw_ostream &out, const TBDGenDescriptor &desc); SourceLoc extractNearestSourceLoc(const TBDGenDescriptor &desc); -using TBDFileAndSymbols = - std::pair>; +using TBDFile = llvm::MachO::InterfaceFile; -/// Computes the TBD file and public symbols for a given module or file. +/// Computes the TBD file for a given Swift module or file. class GenerateTBDRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + TBDFile evaluate(Evaluator &evaluator, TBDGenDescriptor desc) const; +}; + +/// Retrieve the public symbols for a file or module. +class PublicSymbolsRequest + : public SimpleRequest(TBDGenDescriptor), RequestFlags::Uncached> { public: using SimpleRequest::SimpleRequest; @@ -92,7 +113,8 @@ class GenerateTBDRequest friend SimpleRequest; // Evaluation. - TBDFileAndSymbols evaluate(Evaluator &evaluator, TBDGenDescriptor desc) const; + std::vector + evaluate(Evaluator &evaluator, TBDGenDescriptor desc) const; }; /// Report that a request of the given kind is being evaluated, so it diff --git a/include/swift/AST/TBDGenTypeIDZone.def b/include/swift/AST/TBDGenTypeIDZone.def index 255499a2cb13a..b175b8ac19bd3 100644 --- a/include/swift/AST/TBDGenTypeIDZone.def +++ b/include/swift/AST/TBDGenTypeIDZone.def @@ -14,5 +14,8 @@ // //===----------------------------------------------------------------------===// -SWIFT_REQUEST(TBDGen, GenerateTBDRequest, TBDFileAndSymbols(TBDGenDescriptor), +SWIFT_REQUEST(TBDGen, GenerateTBDRequest, TBDFile(TBDGenDescriptor), + Uncached, NoLocationInfo) +SWIFT_REQUEST(TBDGen, PublicSymbolsRequest, + std::vector(TBDGenDescriptor), Uncached, NoLocationInfo) diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 3b0ac9c9d94f9..c7cb46a6044f5 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -1,4 +1,4 @@ -//===--- Type.h - Swift Language Type ASTs ----------------------*- C++ -*-===// +//===--- Type.h - Value objects for Swift and SIL types ---------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,7 +10,10 @@ // //===----------------------------------------------------------------------===// // -// This file defines the Type class. +// This file defines the Type and CanType classes, which are value objects +// used to cheaply pass around different kinds of types. The full hierarchy for +// Swift and SIL types -- including tuple types, function types and more -- is +// defined in Types.h. // //===----------------------------------------------------------------------===// @@ -213,7 +216,10 @@ class Type { bool isNull() const { return Ptr == 0; } - TypeBase *operator->() const { return Ptr; } + TypeBase *operator->() const { + assert(Ptr && "Cannot dereference a null Type!"); + return Ptr; + } explicit operator bool() const { return Ptr != 0; } @@ -530,7 +536,10 @@ public: \ TYPE *getPointer() const { \ return static_cast(Type::getPointer()); \ } \ - TYPE *operator->() const { return getPointer(); } \ + TYPE *operator->() const { \ + assert(getPointer() && "Cannot dereference a null " #TYPE); \ + return getPointer(); \ + } \ operator TYPE *() const { return getPointer(); } \ explicit operator bool() const { return getPointer() != nullptr; } diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 540b999668150..f067218db105e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -58,18 +58,17 @@ class StorageImplInfo; /// Display a nominal type or extension thereof. void simple_display( - llvm::raw_ostream &out, - const llvm::PointerUnion &value); + llvm::raw_ostream &out, + const llvm::PointerUnion &value); /// Request the type from the ith entry in the inheritance clause for the /// given declaration. -class InheritedTypeRequest : - public SimpleRequest, - unsigned, - TypeResolutionStage), - RequestFlags::SeparatelyCached> -{ +class InheritedTypeRequest + : public SimpleRequest< + InheritedTypeRequest, + Type(llvm::PointerUnion, + unsigned, TypeResolutionStage), + RequestFlags::SeparatelyCached> { public: using SimpleRequest::SimpleRequest; @@ -79,9 +78,8 @@ class InheritedTypeRequest : // Evaluation. Type evaluate(Evaluator &evaluator, - llvm::PointerUnion decl, - unsigned index, - TypeResolutionStage stage) const; + llvm::PointerUnion decl, + unsigned index, TypeResolutionStage stage) const; public: // Source location @@ -398,6 +396,9 @@ struct WhereClauseOwner { WhereClauseOwner(DeclContext *dc, GenericParamList *genericParams) : dc(dc), source(genericParams) {} + WhereClauseOwner(DeclContext *dc, TrailingWhereClause *where) + : dc(dc), source(where) {} + WhereClauseOwner(DeclContext *dc, SpecializeAttr *attr) : dc(dc), source(attr) {} @@ -894,9 +895,9 @@ class TypeCheckFunctionBodyRequest : /// Request to typecheck a function body element at the given source location. /// /// Produces true if an error occurred, false otherwise. -class TypeCheckFunctionBodyAtLocRequest - : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -905,8 +906,7 @@ class TypeCheckFunctionBodyAtLocRequest friend SimpleRequest; // Evaluation. - bool evaluate(Evaluator &evaluator, AbstractFunctionDecl *func, - SourceLoc Loc) const; + bool evaluate(Evaluator &evaluator, DeclContext *DC, SourceLoc Loc) const; }; /// Request to obtain a list of stored properties in a nominal type. @@ -1823,6 +1823,45 @@ class ValueWitnessRequest void cacheResult(Witness value) const; }; +struct PreCheckFunctionBuilderDescriptor { + AnyFunctionRef Fn; + +private: + // NOTE: Since source tooling (e.g. code completion) might replace the body, + // we need to take the body into account to calculate 'hash_value' and '=='. + // Also, we cannot 'getBody()' inside 'hash_value' and '==' because it invokes + // another request (even if it's cached). + BraceStmt *Body; + +public: + PreCheckFunctionBuilderDescriptor(AnyFunctionRef Fn) + : Fn(Fn), Body(Fn.getBody()) {} + + friend llvm::hash_code + hash_value(const PreCheckFunctionBuilderDescriptor &owner) { + return llvm::hash_combine(owner.Fn, owner.Body); + } + + friend bool operator==(const PreCheckFunctionBuilderDescriptor &lhs, + const PreCheckFunctionBuilderDescriptor &rhs) { + return lhs.Fn == rhs.Fn && lhs.Body == rhs.Body; + } + + friend bool operator!=(const PreCheckFunctionBuilderDescriptor &lhs, + const PreCheckFunctionBuilderDescriptor &rhs) { + return !(lhs == rhs); + } + + friend SourceLoc extractNearestSourceLoc(PreCheckFunctionBuilderDescriptor d) { + return extractNearestSourceLoc(d.Fn); + } + + friend void simple_display(llvm::raw_ostream &out, + const PreCheckFunctionBuilderDescriptor &d) { + simple_display(out, d.Fn); + } +}; + enum class FunctionBuilderBodyPreCheck : uint8_t { /// There were no problems pre-checking the closure. Okay, @@ -1836,7 +1875,8 @@ enum class FunctionBuilderBodyPreCheck : uint8_t { class PreCheckFunctionBuilderRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -1846,7 +1886,7 @@ class PreCheckFunctionBuilderRequest // Evaluation. FunctionBuilderBodyPreCheck - evaluate(Evaluator &evaluator, AnyFunctionRef fn) const; + evaluate(Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const; public: // Separate caching. diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index f940dc811a05a..0252352960631 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -105,8 +105,8 @@ SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest, SmallVector, bool), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InheritedTypeRequest, - Type(llvm::PointerUnion, unsigned, - TypeResolutionStage), + Type(llvm::PointerUnion, + unsigned, TypeResolutionStage), SeparatelyCached, HasNearestLocation) SWIFT_REQUEST(TypeChecker, InheritsSuperclassInitializersRequest, bool(ClassDecl *), SeparatelyCached, NoLocationInfo) @@ -207,8 +207,8 @@ SWIFT_REQUEST(TypeChecker, TangentStoredPropertyRequest, llvm::Expected(VarDecl *, CanType), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyRequest, bool(AbstractFunctionDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyAtLocRequest, - bool(AbstractFunctionDecl *, SourceLoc), +SWIFT_REQUEST(TypeChecker, TypeCheckASTNodeAtLocRequest, + bool(DeclContext *, SourceLoc), Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, UnderlyingTypeRequest, Type(TypeAliasDecl *), SeparatelyCached, NoLocationInfo) @@ -231,7 +231,7 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(AnyFunctionRef), + FunctionBuilderClosurePreCheck(PreCheckFunctionBuilderDescriptor), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction), diff --git a/include/swift/AST/TypeDifferenceVisitor.h b/include/swift/AST/TypeDifferenceVisitor.h index 3492c33151e46..bdad0415948bc 100644 --- a/include/swift/AST/TypeDifferenceVisitor.h +++ b/include/swift/AST/TypeDifferenceVisitor.h @@ -214,7 +214,7 @@ class CanTypeDifferenceVisitor : public CanTypePairVisitor { bool visitAnyFunctionType(CanAnyFunctionType type1, CanAnyFunctionType type2) { - if (type1->getExtInfo() != type2->getExtInfo()) + if (!type1->hasSameExtInfoAs(type2)) return asImpl().visitDifferentTypeStructure(type1, type2); if (asImpl().visit(type1.getResult(), type2.getResult())) @@ -242,10 +242,10 @@ class CanTypeDifferenceVisitor : public CanTypePairVisitor { bool visitSILFunctionTypeStructure(CanSILFunctionType type1, CanSILFunctionType type2) { - if (type1->getExtInfo() != type2->getExtInfo() || + if (!type1->hasSameExtInfoAs(type2) || type1->getCoroutineKind() != type2->getCoroutineKind() || - type1->getInvocationGenericSignature() - != type2->getInvocationGenericSignature()) + type1->getInvocationGenericSignature() != + type2->getInvocationGenericSignature()) return asImpl().visitDifferentTypeStructure(type1, type2); return false; } diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 3e618c88802b2..022ba7f79d0ce 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -482,12 +482,14 @@ class FunctionTypeRepr : public TypeRepr { TupleTypeRepr *ArgsTy; TypeRepr *RetTy; - SourceLoc ArrowLoc; + SourceLoc AsyncLoc; SourceLoc ThrowsLoc; + SourceLoc ArrowLoc; public: FunctionTypeRepr(GenericParamList *genericParams, TupleTypeRepr *argsTy, - SourceLoc throwsLoc, SourceLoc arrowLoc, TypeRepr *retTy, + SourceLoc asyncLoc, SourceLoc throwsLoc, SourceLoc arrowLoc, + TypeRepr *retTy, GenericParamList *patternGenericParams = nullptr, ArrayRef patternSubs = {}, ArrayRef invocationSubs = {}) @@ -497,7 +499,7 @@ class FunctionTypeRepr : public TypeRepr { PatternGenericParams(patternGenericParams), PatternGenericEnv(nullptr), PatternSubs(patternSubs), ArgsTy(argsTy), RetTy(retTy), - ArrowLoc(arrowLoc), ThrowsLoc(throwsLoc) { + AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc) { } GenericParamList *getGenericParams() const { return GenericParams; } @@ -527,10 +529,12 @@ class FunctionTypeRepr : public TypeRepr { TupleTypeRepr *getArgsTypeRepr() const { return ArgsTy; } TypeRepr *getResultTypeRepr() const { return RetTy; } - bool throws() const { return ThrowsLoc.isValid(); } + bool isAsync() const { return AsyncLoc.isValid(); } + bool isThrowing() const { return ThrowsLoc.isValid(); } - SourceLoc getArrowLoc() const { return ArrowLoc; } + SourceLoc getAsyncLoc() const { return AsyncLoc; } SourceLoc getThrowsLoc() const { return ThrowsLoc; } + SourceLoc getArrowLoc() const { return ArrowLoc; } static bool classof(const TypeRepr *T) { return T->getKind() == TypeReprKind::Function; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 34d48b6e27622..0f911f4fb5ae4 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// // -// This file defines the TypeBase class and subclasses. +// This file defines the TypeBase class and subclasses, which describe the Swift +// and SIL ASTs. See also: Type.h. // //===----------------------------------------------------------------------===// @@ -19,6 +20,7 @@ #include "swift/AST/AutoDiff.h" #include "swift/AST/DeclContext.h" +#include "swift/AST/ExtInfo.h" #include "swift/AST/GenericParamKey.h" #include "swift/AST/Identifier.h" #include "swift/AST/Ownership.h" @@ -42,11 +44,6 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" -namespace clang { -class Type; -class FunctionType; -} // namespace clang - namespace llvm { struct fltSemantics; } // namespace llvm @@ -282,7 +279,9 @@ enum class TypeMatchFlags { }; using TypeMatchOptions = OptionSet; -/// TypeBase - Base class for all types in Swift. +/// Base class for all types which describe the Swift and SIL ASTs. +/// +/// See TypeNodes.def for a succinct description of the full class hierarchy. class alignas(1 << TypeAlignInBits) TypeBase { friend class ASTContext; @@ -308,7 +307,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { } protected: - enum { NumAFTExtInfoBits = 8 }; + enum { NumAFTExtInfoBits = 9 }; enum { NumSILExtInfoBits = 8 }; union { uint64_t OpaqueBits; @@ -342,7 +341,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// Extra information which affects how the function is called, like /// regparm and the calling convention. ExtInfoBits : NumAFTExtInfoBits, - HasUncommonInfo : 1, + HasClangTypeInfo : 1, : NumPadBits, NumParams : 16 ); @@ -365,7 +364,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+3+1+2+1+1, ExtInfoBits : NumSILExtInfoBits, - HasUncommonInfo : 1, + HasClangTypeInfo : 1, CalleeConvention : 3, HasErrorResult : 1, CoroutineKind : 2, @@ -711,7 +710,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// class-bounded existential type whose required conformances are /// all @objc. Such types are compatible with ObjC. bool isObjCExistentialType(); - + + // // Is this an ObjC generic class. + bool isTypeErasedGenericClassType(); + /// Determines whether this type is an existential type with a class protocol /// bound. bool isClassExistentialType(); @@ -2176,6 +2178,8 @@ END_CAN_TYPE_WRAPPER(TupleType, Type) /// UnboundGenericType - Represents a generic type where the type arguments have /// not yet been resolved. +/// +/// This type is on its way out. Try to avoid introducing new usages. class UnboundGenericType : public AnyGenericType, public llvm::FoldingSetNode { private: @@ -2685,116 +2689,6 @@ BEGIN_CAN_TYPE_WRAPPER(DynamicSelfType, Type) } END_CAN_TYPE_WRAPPER(DynamicSelfType, Type) -/// A language-level calling convention. -enum class SILFunctionLanguage : uint8_t { - /// A variation of the Swift calling convention. - Swift = 0, - - /// A variation of the C calling convention. - C, -}; - -/// The representation form of a function. -enum class FunctionTypeRepresentation : uint8_t { - /// A "thick" function that carries a context pointer to reference captured - /// state. The default native function representation. - Swift = 0, - - /// A thick function that is represented as an Objective-C block. - Block, - - /// A "thin" function that needs no context. - Thin, - - /// A C function pointer (or reference), which is thin and also uses the C - /// calling convention. - CFunctionPointer, - - /// The value of the greatest AST function representation. - Last = CFunctionPointer, -}; - -/// The representation form of a SIL function. -/// -/// This is a superset of FunctionTypeRepresentation. The common representations -/// must share an enum value. -/// -/// TODO: The overlap of SILFunctionTypeRepresentation and -/// FunctionTypeRepresentation is a total hack necessitated by the way SIL -/// TypeLowering is currently written. We ought to refactor TypeLowering so that -/// it is not necessary to distinguish these cases. -enum class SILFunctionTypeRepresentation : uint8_t { - /// A freestanding thick function. - Thick = uint8_t(FunctionTypeRepresentation::Swift), - - /// A thick function that is represented as an Objective-C block. - Block = uint8_t(FunctionTypeRepresentation::Block), - - /// A freestanding thin function that needs no context. - Thin = uint8_t(FunctionTypeRepresentation::Thin), - - /// A C function pointer, which is thin and also uses the C calling - /// convention. - CFunctionPointer = uint8_t(FunctionTypeRepresentation::CFunctionPointer), - - /// The value of the greatest AST function representation. - LastAST = CFunctionPointer, - - /// The value of the least SIL-only function representation. - FirstSIL = 8, - - /// A Swift instance method. - Method = FirstSIL, - - /// An Objective-C method. - ObjCMethod, - - /// A Swift protocol witness. - WitnessMethod, - - /// A closure invocation function that has not been bound to a context. - Closure, -}; - -/// Can this calling convention result in a function being called indirectly -/// through the runtime. -inline bool canBeCalledIndirectly(SILFunctionTypeRepresentation rep) { - switch (rep) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::Closure: - return false; - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::WitnessMethod: - return true; - } - - llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); -} - -/// Map a SIL function representation to the base language calling convention -/// it uses. -inline SILFunctionLanguage -getSILFunctionLanguage(SILFunctionTypeRepresentation rep) { - switch (rep) { - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - return SILFunctionLanguage::C; - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::WitnessMethod: - case SILFunctionTypeRepresentation::Closure: - return SILFunctionLanguage::Swift; - } - - llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); -} - /// AnyFunctionType - A function type has zero or more input parameters and a /// single result. The result type may be a tuple. For example: /// "(int) -> int" or "(a : int, b : int) -> (int, int)". @@ -2911,6 +2805,8 @@ class AnyFunctionType : public TypeBase { } }; + using ExtInfo = swift::ASTExtInfo; + using ExtInfoBuilder = swift::ASTExtInfoBuilder; using CanParamArrayRef = ArrayRefView; @@ -2966,221 +2862,25 @@ class AnyFunctionType : public TypeBase { } }; - /// A class which abstracts out some details necessary for - /// making a call. - class ExtInfo { - // If bits are added or removed, then TypeBase::AnyFunctionTypeBits - // and NumMaskBits must be updated, and they must match. - // - // |representation|noEscape|throws|differentiability| - // | 0 .. 3 | 4 | 5 | 6 .. 7 | - // - enum : unsigned { - RepresentationMask = 0xF << 0, - NoEscapeMask = 1 << 4, - ThrowsMask = 1 << 5, - DifferentiabilityMaskOffset = 6, - DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, - NumMaskBits = 8 - }; - - unsigned Bits; // Naturally sized for speed. - - public: - class Uncommon { - friend ExtInfo; - friend class AnyFunctionType; - friend class FunctionType; - // We preserve a full clang::Type *, not a clang::FunctionType * as: - // 1. We need to keep sugar in case we need to present an error to the user. - // 2. The actual type being stored is [ignoring sugar] either a - // clang::PointerType, a clang::BlockPointerType, or a - // clang::ReferenceType which points to a clang::FunctionType. - const clang::Type *ClangFunctionType; - - 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: - Uncommon Other; - - static void assertIsFunctionType(const clang::Type *); - - ExtInfo(unsigned Bits, Uncommon Other) : Bits(Bits), Other(Other) { - // TODO: [store-sil-clang-function-type] Once we start serializing - // the Clang type, we should also assert that the pointer is non-null. - auto Rep = Representation(Bits & RepresentationMask); - if ((Rep == Representation::CFunctionPointer) && Other.ClangFunctionType) - assertIsFunctionType(Other.ClangFunctionType); - } - - friend AnyFunctionType; - friend class FunctionType; - - public: - // Constructor with all defaults. - ExtInfo() - : ExtInfo(Representation::Swift, false, false, - DifferentiabilityKind::NonDifferentiable, - nullptr) { - } - - // Constructor for polymorphic type. - ExtInfo(Representation Rep, bool Throws) - : ExtInfo(Rep, false, Throws, DifferentiabilityKind::NonDifferentiable, - nullptr) { - } - - // Constructor with no defaults. - ExtInfo(Representation Rep, bool IsNoEscape, bool Throws, - DifferentiabilityKind DiffKind, - const clang::Type *type) - : ExtInfo(((unsigned) Rep) - | (IsNoEscape ? NoEscapeMask : 0) - | (Throws ? ThrowsMask : 0) - | (((unsigned)DiffKind << DifferentiabilityMaskOffset) - & DifferentiabilityMask), - Uncommon(type)) { - } - - bool isNoEscape() const { return Bits & NoEscapeMask; } - bool throws() const { return Bits & ThrowsMask; } - bool isDifferentiable() const { - return getDifferentiabilityKind() > - DifferentiabilityKind::NonDifferentiable; - } - DifferentiabilityKind getDifferentiabilityKind() const { - return DifferentiabilityKind((Bits & DifferentiabilityMask) >> - DifferentiabilityMaskOffset); - } - Representation getRepresentation() const { - unsigned rawRep = Bits & RepresentationMask; - assert(rawRep <= unsigned(Representation::Last) - && "unexpected SIL representation"); - return Representation(rawRep); - } - - /// Return the underlying Uncommon value if it is not the default value. - Optional getUncommonInfo() const { - return Other.empty() ? Optional() : Other; - } - - bool hasSelfParam() const { - switch (getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Closure: - return false; - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::WitnessMethod: - return true; - } - - llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); - } - - /// True if the function representation carries context. - bool hasContext() const { - switch (getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Block: - return true; - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::WitnessMethod: - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Closure: - return false; - } - - llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); - } - - // Note that we don't have setters. That is by design, use - // the following with methods instead of mutating these objects. - LLVM_NODISCARD - ExtInfo withRepresentation(Representation Rep) const { - return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep, Other); - } - LLVM_NODISCARD - ExtInfo withNoEscape(bool NoEscape = true) const { - return ExtInfo(NoEscape ? (Bits | NoEscapeMask) : (Bits & ~NoEscapeMask), - Other); - } - LLVM_NODISCARD - ExtInfo withThrows(bool Throws = true) const { - return ExtInfo(Throws ? (Bits | ThrowsMask) : (Bits & ~ThrowsMask), - Other); - } - LLVM_NODISCARD - ExtInfo withClangFunctionType(const clang::Type *type) const { - return ExtInfo(Bits, Uncommon(type)); - } - LLVM_NODISCARD - ExtInfo - withDifferentiabilityKind(DifferentiabilityKind differentiability) const { - return ExtInfo( - (Bits & ~DifferentiabilityMask) | - ((unsigned)differentiability << DifferentiabilityMaskOffset), - Other); - } - - std::pair getFuncAttrKey() const { - return std::make_pair(Bits, Other.ClangFunctionType); - } - - /// Put a SIL representation in the ExtInfo. - /// - /// SIL type lowering transiently generates AST function types with SIL - /// representations. However, they shouldn't persist in the AST, and - /// don't need to be parsed, printed, or serialized. - ExtInfo withSILRepresentation(SILFunctionTypeRepresentation Rep) const { - return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep, Other); - } - - SILFunctionTypeRepresentation getSILRepresentation() const { - unsigned rawRep = Bits & RepresentationMask; - return SILFunctionTypeRepresentation(rawRep); - } - - bool operator==(ExtInfo Other) const { - return Bits == Other.Bits; - } - bool operator!=(ExtInfo Other) const { - return Bits != Other.Bits; - } - }; - protected: /// Create an AnyFunctionType. /// /// Subclasses are responsible for storing and retrieving the - /// ExtInfo::Uncommon value if one is present. + /// ClangTypeInfo value if one is present. AnyFunctionType(TypeKind Kind, const ASTContext *CanTypeContext, Type Output, RecursiveTypeProperties properties, unsigned NumParams, ExtInfo Info) : TypeBase(Kind, CanTypeContext, properties), Output(Output) { - Bits.AnyFunctionType.ExtInfoBits = Info.Bits; - Bits.AnyFunctionType.HasUncommonInfo = Info.getUncommonInfo().hasValue(); + Bits.AnyFunctionType.ExtInfoBits = Info.getBits(); + Bits.AnyFunctionType.HasClangTypeInfo = Info.getClangTypeInfo().hasValue(); Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); // The use of both assert() and static_assert() is intentional. - assert(Bits.AnyFunctionType.ExtInfoBits == Info.Bits - && "Bits were dropped!"); - static_assert(ExtInfo::NumMaskBits == NumAFTExtInfoBits, - "ExtInfo and AnyFunctionTypeBitfields must agree on bit size"); + assert(Bits.AnyFunctionType.ExtInfoBits == Info.getBits() && + "Bits were dropped!"); + static_assert( + ASTExtInfoBuilder::NumMaskBits == NumAFTExtInfoBits, + "ExtInfo and AnyFunctionTypeBitfields must agree on bit size"); } public: @@ -3216,15 +2916,15 @@ class AnyFunctionType : public TypeBase { GenericSignature getOptGenericSignature() const; - bool hasClangFunctionType() const { - return Bits.AnyFunctionType.HasUncommonInfo; + bool hasClangTypeInfo() const { + return Bits.AnyFunctionType.HasClangTypeInfo; } - const clang::Type *getClangFunctionType() const; - const clang::Type *getCanonicalClangFunctionType() const; + ClangTypeInfo getClangTypeInfo() const; + ClangTypeInfo getCanonicalClangTypeInfo() const; ExtInfo getExtInfo() const { - return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangFunctionType()); + return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangTypeInfo()); } /// Get the canonical ExtInfo for the function type. @@ -3233,10 +2933,12 @@ class AnyFunctionType : public TypeBase { /// In the future, we will always use the canonical clang function type. ExtInfo getCanonicalExtInfo(bool useClangFunctionType) const { return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, - useClangFunctionType ? getCanonicalClangFunctionType() - : nullptr); + useClangFunctionType ? getCanonicalClangTypeInfo() + : ClangTypeInfo()); } + bool hasSameExtInfoAs(const AnyFunctionType *otherFn); + /// Get the representation of the function type. Representation getRepresentation() const { return getExtInfo().getRepresentation(); @@ -3377,9 +3079,9 @@ class AnyFunctionType : public TypeBase { return getExtInfo().isNoEscape(); } - bool throws() const { - return getExtInfo().throws(); - } + bool isAsync() const { return getExtInfo().isAsync(); } + + bool isThrowing() const { return getExtInfo().isThrowing(); } bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } DifferentiabilityKind getDifferentiabilityKind() const { @@ -3406,6 +3108,7 @@ class AnyFunctionType : public TypeBase { }; BEGIN_CAN_TYPE_WRAPPER(AnyFunctionType, Type) using ExtInfo = AnyFunctionType::ExtInfo; + using ExtInfoBuilder = AnyFunctionType::ExtInfoBuilder; using CanParamArrayRef = AnyFunctionType::CanParamArrayRef; static CanAnyFunctionType get(CanGenericSignature signature, @@ -3433,19 +3136,20 @@ inline AnyFunctionType::CanYield AnyFunctionType::Yield::getCanonical() const { /// /// For example: /// let x : (Float, Int) -> Int -class FunctionType final : public AnyFunctionType, - public llvm::FoldingSetNode, - private llvm::TrailingObjects { +class FunctionType final + : public AnyFunctionType, + public llvm::FoldingSetNode, + private llvm::TrailingObjects { friend TrailingObjects; - + size_t numTrailingObjects(OverloadToken) const { return getNumParams(); } - size_t numTrailingObjects(OverloadToken) const { - return hasClangFunctionType() ? 1 : 0; + size_t numTrailingObjects(OverloadToken) const { + return hasClangTypeInfo() ? 1 : 0; } public: @@ -3458,12 +3162,13 @@ class FunctionType final : public AnyFunctionType, return {getTrailingObjects(), getNumParams()}; } - const clang::Type *getClangFunctionType() const { - if (!hasClangFunctionType()) - return nullptr; - auto *type = getTrailingObjects()->ClangFunctionType; - assert(type && "If the pointer was null, we shouldn't have stored it."); - return type; + ClangTypeInfo getClangTypeInfo() const { + if (!hasClangTypeInfo()) + return ClangTypeInfo(); + auto *info = getTrailingObjects(); + assert(!info->empty() && + "If the ClangTypeInfo was empty, we shouldn't have stored it."); + return *info; } void Profile(llvm::FoldingSetNodeID &ID) { @@ -3499,6 +3204,7 @@ END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType) /// has a default argument. struct ParameterListInfo { SmallBitVector defaultArguments; + SmallBitVector acceptsUnlabeledTrailingClosures; public: ParameterListInfo() { } @@ -3509,6 +3215,10 @@ struct ParameterListInfo { /// Whether the parameter at the given index has a default argument. bool hasDefaultArgument(unsigned paramIdx) const; + /// Whether the parameter accepts an unlabeled trailing closure argument + /// according to the "forward-scan" rule. + bool acceptsUnlabeledTrailingClosureArgument(unsigned paramIdx) const; + /// Retrieve the number of non-defaulted parameters. unsigned numNonDefaultedParameters() const { return defaultArguments.count(); @@ -4168,10 +3878,12 @@ namespace Lowering { /// This type is defined by the AST library because it must be capable /// of appearing in secondary positions, e.g. within tuple and /// function parameter and result types. -class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, - private llvm::TrailingObjects { +class SILFunctionType final + : public TypeBase, + public llvm::FoldingSetNode, + private llvm::TrailingObjects { friend TrailingObjects; size_t numTrailingObjects(OverloadToken) const { @@ -4191,178 +3903,15 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, } size_t numTrailingObjects(OverloadToken) const { - return size_t(hasPatternSubstitutions()) - + size_t(hasInvocationSubstitutions()); + return size_t(hasPatternSubstitutions()) + + size_t(hasInvocationSubstitutions()); } public: - using Language = SILFunctionLanguage; - using Representation = SILFunctionTypeRepresentation; - - /// A class which abstracts out some details necessary for - /// making a call. - class ExtInfo { - // If bits are added or removed, then TypeBase::SILFunctionTypeBits - // and NumMaskBits must be updated, and they must match. - - // |representation|pseudogeneric|noescape|differentiability| - // | 0 .. 3 | 4 | 5 | 6 .. 7 | - // - enum : unsigned { - RepresentationMask = 0xF << 0, - PseudogenericMask = 1 << 4, - NoEscapeMask = 1 << 5, - DifferentiabilityMaskOffset = 6, - DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, - NumMaskBits = 8 - }; - - unsigned Bits; // Naturally sized for speed. - - class Uncommon { - friend ExtInfo; - friend class SILFunctionType; - - // Invariant: The FunctionType is canonical. - // We store a clang::FunctionType * instead of a clang::CanQualType to - // avoid depending on the Clang AST in this header. - const clang::FunctionType *ClangFunctionType; - - 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; - - ExtInfo(unsigned Bits, Uncommon Other) : Bits(Bits), Other(Other) {} - - friend class SILFunctionType; - public: - // Constructor with all defaults. - ExtInfo() : Bits(0), Other(Uncommon(nullptr)) { } - - // Constructor for polymorphic type. - ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape, - DifferentiabilityKind diffKind, - const clang::FunctionType *type) - : ExtInfo(((unsigned) rep) - | (isPseudogeneric ? PseudogenericMask : 0) - | (isNoEscape ? NoEscapeMask : 0) - | (((unsigned)diffKind << DifferentiabilityMaskOffset) - & DifferentiabilityMask), - Uncommon(type)) { - } - - static ExtInfo getThin() { - return ExtInfo(Representation::Thin, false, false, - DifferentiabilityKind::NonDifferentiable, nullptr); - } - - /// Is this function pseudo-generic? A pseudo-generic function - /// is not permitted to dynamically depend on its type arguments. - bool isPseudogeneric() const { return Bits & PseudogenericMask; } - - // Is this function guaranteed to be no-escape by the type system? - bool isNoEscape() const { return Bits & NoEscapeMask; } - - bool isDifferentiable() const { - return getDifferentiabilityKind() != - DifferentiabilityKind::NonDifferentiable; - } - - DifferentiabilityKind getDifferentiabilityKind() const { - return DifferentiabilityKind((Bits & DifferentiabilityMask) >> - DifferentiabilityMaskOffset); - } - - /// What is the abstract representation of this function value? - Representation getRepresentation() const { - return Representation(Bits & RepresentationMask); - } - Language getLanguage() const { - 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: - case Representation::Block: - case Representation::Thin: - case Representation::CFunctionPointer: - case Representation::Closure: - return false; - case Representation::ObjCMethod: - case Representation::Method: - case Representation::WitnessMethod: - return true; - } - - llvm_unreachable("Unhandled Representation in switch."); - } - - /// True if the function representation carries context. - bool hasContext() const { - switch (getRepresentation()) { - case Representation::Thick: - case Representation::Block: - return true; - case Representation::Thin: - case Representation::CFunctionPointer: - case Representation::ObjCMethod: - case Representation::Method: - case Representation::WitnessMethod: - case Representation::Closure: - return false; - } - - llvm_unreachable("Unhandled Representation in switch."); - } - - // Note that we don't have setters. That is by design, use - // the following with methods instead of mutating these objects. - ExtInfo withRepresentation(Representation Rep) const { - return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep, Other); - } - ExtInfo withIsPseudogeneric(bool isPseudogeneric = true) const { - return ExtInfo(isPseudogeneric - ? (Bits | PseudogenericMask) - : (Bits & ~PseudogenericMask), - Other); - } - ExtInfo withNoEscape(bool NoEscape = true) const { - return ExtInfo(NoEscape ? (Bits | NoEscapeMask) : (Bits & ~NoEscapeMask), - Other); - } - ExtInfo - withDifferentiabilityKind(DifferentiabilityKind differentiability) const { - return ExtInfo( - (Bits & ~DifferentiabilityMask) | - ((unsigned)differentiability << DifferentiabilityMaskOffset), - Other); - } - - std::pair getFuncAttrKey() const { - return std::make_pair(Bits, Other.ClangFunctionType); - } - - bool operator==(ExtInfo Other) const { - return Bits == Other.Bits; - } - bool operator!=(ExtInfo Other) const { - return Bits != Other.Bits; - } - }; + using ExtInfo = SILExtInfo; + using ExtInfoBuilder = SILExtInfoBuilder; + using Language = SILExtInfoBuilder::Language; + using Representation = SILExtInfoBuilder::Representation; private: unsigned NumParameters; @@ -4372,7 +3921,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, unsigned NumAnyResults : 16; // Not including the ErrorResult. unsigned NumAnyIndirectFormalResults : 16; // Subset of NumAnyResults. - // [SILFunctionType-layout] + // [NOTE: SILFunctionType-layout] // The layout of a SILFunctionType in memory is: // SILFunctionType // SILParameterInfo[NumParameters] @@ -4747,7 +4296,9 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return WitnessMethodConformance; } - const clang::FunctionType *getClangFunctionType() const; + ClangTypeInfo getClangTypeInfo() const; + + bool hasSameExtInfoAs(const SILFunctionType *otherFn); /// Given that `this` is a `@differentiable` or `@differentiable(linear)` /// function type, returns an `IndexSubset` corresponding to the @@ -4895,7 +4446,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, CanGenericSignature transposeFunctionGenericSignature = nullptr); ExtInfo getExtInfo() const { - return ExtInfo(Bits.SILFunctionType.ExtInfoBits, getClangFunctionType()); + return ExtInfo(Bits.SILFunctionType.ExtInfoBits, getClangTypeInfo()); } /// Returns the language-level calling convention of the function. diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 9456b729871b3..0dee7442f16c6 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -235,6 +235,9 @@ namespace swift { /// Enable experimental #assert feature. bool EnableExperimentalStaticAssert = false; + /// Enable experimental concurrency model. + bool EnableExperimentalConcurrency = false; + /// Should we check the target OSs of serialized modules to see that they're /// new enough? bool EnableTargetOSChecking = true; @@ -271,11 +274,18 @@ namespace swift { /// behavior. This is a staging flag, and will be removed in the future. bool EnableNewOperatorLookup = false; + /// Whether to enable the "fuzzy" forward-scanning behavior for trailing + /// closure matching, which skips over defaulted closure parameters + /// to match later (non-defaulted) closure parameters + /// + /// This is a backward-compatibility hack for unlabeled trailing closures, + /// to be disabled in Swift 6+. + bool EnableFuzzyForwardScanTrailingClosureMatching = true; + /// Use Clang function types for computing canonical types. /// If this option is false, the clang function types will still be computed /// but will not be used for checking type equality. - /// FIXME: [clang-function-type-serialization] This option should be turned - /// on once we start serializing clang function types. + /// [TODO: Clang-type-plumbing] Turn on for feature rollout. bool UseClangFunctionTypes = false; /// Whether to use the import as member inference system @@ -372,6 +382,9 @@ namespace swift { /// Enable verification when every SubstitutionMap is constructed. bool VerifyAllSubstitutionMaps = false; + /// Load swiftmodule files in memory as volatile and avoid mmap. + bool EnableVolatileModules = false; + /// Sets the target we are building for and updates platform conditions /// to match. /// @@ -504,7 +517,7 @@ namespace swift { /// Indicate that the type checker should skip type-checking non-inlinable /// function bodies. bool SkipNonInlinableFunctionBodies = false; - + /// /// Flags for developers /// diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index eb21ea7a29714..2595b68af0b34 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -16,6 +16,7 @@ #include "swift/Basic/LLVM.h" #include "swift/Config.h" #include "llvm/ADT/StringRef.h" +#include "clang/Driver/DarwinSDKInfo.h" namespace llvm { class Triple; @@ -71,11 +72,6 @@ namespace swift { /// Returns the platform Kind for Darwin triples. DarwinPlatformKind getDarwinPlatformKind(const llvm::Triple &triple); - /// Maps an arbitrary platform to its non-simulator equivalent. - /// - /// If \p platform is not a simulator platform, it will be returned as is. - DarwinPlatformKind getNonSimulatorPlatform(DarwinPlatformKind platform); - /// Returns the architecture component of the path for a given target triple. /// /// Typically this is used for mapping the architecture component of the @@ -106,6 +102,10 @@ namespace swift { /// LLVM target triple. Optional getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple); + + /// Retrieve the target SDK version for the given SDKInfo and target triple. + llvm::VersionTuple getTargetSDKVersion(clang::driver::DarwinSDKInfo &SDKInfo, + const llvm::Triple &triple); } // end namespace swift #endif // SWIFT_BASIC_PLATFORM_H diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h index 80759a78bb0a8..984c19ce790ec 100644 --- a/include/swift/Basic/Statistic.h +++ b/include/swift/Basic/Statistic.h @@ -15,8 +15,8 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/Timer.h" #include "swift/Basic/LLVM.h" -#include "swift/Basic/Timer.h" #include #include diff --git a/include/swift/Basic/Timer.h b/include/swift/Basic/Timer.h deleted file mode 100644 index 0258be791208e..0000000000000 --- a/include/swift/Basic/Timer.h +++ /dev/null @@ -1,66 +0,0 @@ -//===--- Timer.h - Shared timers for compilation phases ---------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BASIC_TIMER_H -#define SWIFT_BASIC_TIMER_H - -#include "swift/Basic/LLVM.h" -#include "llvm/ADT/Optional.h" -#include "llvm/Support/Timer.h" - -namespace swift { - /// A convenience class for declaring a timer that's part of the Swift - /// compilation timers group. - /// - /// Please don't use this class directly for anything other than the flat, - /// top-level compilation-phase timing numbers; unadorned SharedTimers are - /// enabled, summed and reported via -debug-time-compilation, using LLVM's - /// built-in logic for timer groups, and that logic doesn't work right if - /// there's any nesting or reentry in timers at all (crashes on reentry, - /// simply mis-reports nesting). Additional SharedTimers also confuse users - /// who are expecting to see only top-level phase timings when they pass - /// -debug-time-compilation. - /// - /// Instead, please use FrontendStatsTracer objects and the -stats-output-dir - /// subsystem in include/swift/Basic/Statistic.h. In addition to not - /// interfering with users passing -debug-time-compilation, the - /// FrontendStatsTracer objects automatically instantiate nesting-safe and - /// reentry-safe SharedTimers themselves, as well as supporting event and - /// source-entity tracing and profiling. - class SharedTimer { - enum class State { - Initial, - Skipped, - Enabled - }; - static State CompilationTimersEnabled; - - Optional Timer; - - public: - explicit SharedTimer(StringRef name) { - if (CompilationTimersEnabled == State::Enabled) - Timer.emplace(name, name, "swift", "Swift compilation"); - else - CompilationTimersEnabled = State::Skipped; - } - - /// Must be called before any SharedTimers have been created. - static void enableCompilationTimers() { - assert(CompilationTimersEnabled != State::Skipped && - "a timer has already been created"); - CompilationTimersEnabled = State::Enabled; - } - }; -} // end namespace swift - -#endif // SWIFT_BASIC_TIMER_H diff --git a/include/swift/Basic/Unicode.h b/include/swift/Basic/Unicode.h index ff2163eb0f374..4b1108af7fac1 100644 --- a/include/swift/Basic/Unicode.h +++ b/include/swift/Basic/Unicode.h @@ -68,10 +68,6 @@ bool isSingleUnicodeScalar(StringRef S); unsigned extractFirstUnicodeScalar(StringRef S); -/// Get the length of the UTF8 string transcoded into UTF16. -/// Returns the number of code units in UTF16 representation -uint64_t getUTF16Length(StringRef Str); - } // end namespace unicode } // end namespace swift diff --git a/include/swift/Demangling/Demangle.h b/include/swift/Demangling/Demangle.h index 97ebf388957f1..6b05dbad2fc91 100644 --- a/include/swift/Demangling/Demangle.h +++ b/include/swift/Demangling/Demangle.h @@ -62,6 +62,8 @@ struct DemangleOptions { bool DisplayDebuggerGeneratedModule = true; bool DisplayStdlibModule = true; bool DisplayObjCModule = true; + bool PrintForTypeName = false; + /// If this is nonempty, entities in this module name will not be qualified. llvm::StringRef HidingCurrentModule; /// A function to render generic parameter names. diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 5e534c3e901fe..468a0711f90f4 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -249,6 +249,7 @@ NODE(AssociatedConformanceDescriptor) NODE(DefaultAssociatedConformanceAccessor) NODE(BaseConformanceDescriptor) NODE(AssociatedTypeDescriptor) +NODE(AsyncAnnotation) NODE(ThrowsAnnotation) NODE(EmptyList) NODE(FirstElementMarker) @@ -287,9 +288,12 @@ NODE(OpaqueTypeDescriptorAccessorVar) NODE(OpaqueReturnType) CONTEXT_NODE(OpaqueReturnTypeOf) -// Added in Swift 5.3 +// Added in Swift 5.4 NODE(CanonicalSpecializedGenericMetaclass) NODE(CanonicalSpecializedGenericTypeMetadataAccessFunction) +NODE(MetadataInstantiationCache) +NODE(NoncanonicalSpecializedGenericTypeMetadata) +NODE(NoncanonicalSpecializedGenericTypeMetadataCache) #undef CONTEXT_NODE #undef NODE diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 89631c291b36e..e591e978dccd1 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -593,16 +593,29 @@ class TypeDecoder { FunctionMetadataDifferentiabilityKind::Linear); } - bool isThrow = - Node->getChild(0)->getKind() == NodeKind::ThrowsAnnotation; - flags = flags.withThrows(isThrow); + unsigned firstChildIdx = 0; + bool isThrow = false; + if (Node->getChild(firstChildIdx)->getKind() + == NodeKind::ThrowsAnnotation) { + isThrow = true; + ++firstChildIdx; + } + + bool isAsync = false; + if (Node->getChild(firstChildIdx)->getKind() + == NodeKind::AsyncAnnotation) { + isAsync = true; + ++firstChildIdx; + } + + flags = flags.withAsync(isAsync).withThrows(isThrow); - if (isThrow && Node->getNumChildren() < 3) + if (Node->getNumChildren() < firstChildIdx + 2) return BuiltType(); bool hasParamFlags = false; llvm::SmallVector, 8> parameters; - if (!decodeMangledFunctionInputType(Node->getChild(isThrow ? 1 : 0), + if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx), parameters, hasParamFlags)) return BuiltType(); flags = @@ -617,7 +630,7 @@ class TypeDecoder { Node->getKind() == NodeKind::EscapingLinearFunctionType); - auto result = decodeMangledType(Node->getChild(isThrow ? 2 : 1)); + auto result = decodeMangledType(Node->getChild(firstChildIdx+1)); if (!result) return BuiltType(); return Builder.createFunctionType(parameters, result, flags); } @@ -711,7 +724,6 @@ class TypeDecoder { case NodeKind::Tuple: { llvm::SmallVector elements; std::string labels; - bool variadic = false; for (auto &element : *Node) { if (element->getKind() != NodeKind::TupleElement) return BuiltType(); @@ -744,7 +756,7 @@ class TypeDecoder { elements.push_back(elementType); } - return Builder.createTupleType(elements, std::move(labels), variadic); + return Builder.createTupleType(elements, std::move(labels)); } case NodeKind::TupleElement: if (Node->getNumChildren() < 1) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 72555f19f02e0..f5f75f13e7694 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -53,7 +53,7 @@ namespace swift { -class SerializedModuleLoader; +class SerializedModuleLoaderBase; class MemoryBufferSerializedModuleLoader; class SILModule; @@ -128,7 +128,8 @@ class CompilerInvocation { bool parseArgs(ArrayRef Args, DiagnosticEngine &Diags, SmallVectorImpl> *ConfigurationFileBuffers = nullptr, - StringRef workingDirectory = {}); + StringRef workingDirectory = {}, + StringRef mainExecutablePath = {}); /// Sets specific options based on the given serialized Swift binary data. /// @@ -213,8 +214,11 @@ class CompilerInvocation { /// Computes the runtime resource path relative to the given Swift /// executable. static void computeRuntimeResourcePathFromExecutablePath( - StringRef mainExecutablePath, - llvm::SmallString<128> &runtimeResourcePath); + StringRef mainExecutablePath, bool shared, + llvm::SmallVectorImpl &runtimeResourcePath); + + /// Appends `lib/swift[_static]` to the given path + static void appendSwiftLibDir(llvm::SmallVectorImpl &path, bool shared); void setSDKPath(const std::string &Path); @@ -434,7 +438,7 @@ class CompilerInstance { std::unique_ptr Stats; mutable ModuleDecl *MainModule = nullptr; - SerializedModuleLoader *SML = nullptr; + SerializedModuleLoaderBase *DefaultSerializedLoader = nullptr; MemoryBufferSerializedModuleLoader *MemoryBufferLoader = nullptr; /// Contains buffer IDs for input source code files. diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 306df2cb29719..aef89085fb34b 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -161,12 +161,6 @@ class FrontendOptions { /// If set, dumps wall time taken to check each expression. bool DebugTimeExpressionTypeChecking = false; - /// If set, prints the time taken in each major compilation phase to - /// llvm::errs(). - /// - /// \sa swift::SharedTimer - bool DebugTimeCompilation = false; - /// The path to which we should output statistics files. std::string StatsOutputDir; @@ -256,6 +250,9 @@ class FrontendOptions { /// Should we enable the dependency verifier for all primary files known to this frontend? bool EnableIncrementalDependencyVerifier = false; + /// The path of the swift-frontend executable. + std::string MainExecutablePath; + /// The directory path we should use when print #include for the bridging header. /// By default, we include ImplicitObjCHeaderPath directly. llvm::Optional BridgingHeaderDirForPrint; @@ -289,6 +286,11 @@ class FrontendOptions { /// -dump-scope-maps. SmallVector, 2> DumpScopeMapLocations; + /// Determines whether the static or shared resource folder is used. + /// When set to `true`, the default resource folder will be set to + /// '.../lib/swift', otherwise '.../lib/swift_static'. + bool UseSharedResourceFolder = true; + /// \return true if action only parses without doing other compilation steps. static bool shouldActionOnlyParse(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 6c516df214d3d..78116f109a8e8 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -162,14 +162,116 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { ~ExplicitSwiftModuleLoader(); }; +/// Information about explicitly specified Swift module files. +struct ExplicitModuleInfo { + // Path of the .swiftmodule file. + std::string modulePath; + // Path of the .swiftmoduledoc file. + std::string moduleDocPath; + // Path of the .swiftsourceinfo file. + std::string moduleSourceInfoPath; + // Opened buffer for the .swiftmodule file. + std::unique_ptr moduleBuffer; +}; + +/// Parser of explicit module maps passed into the compiler. +// [ +// { +// "moduleName": "A", +// "modulePath": "A.swiftmodule", +// "docPath": "A.swiftdoc", +// "sourceInfoPath": "A.swiftsourceinfo" +// }, +// { +// "moduleName": "B", +// "modulePath": "B.swiftmodule", +// "docPath": "B.swiftdoc", +// "sourceInfoPath": "B.swiftsourceinfo" +// } +// ] +class ExplicitModuleMapParser { +public: + ExplicitModuleMapParser(llvm::BumpPtrAllocator &Allocator) : Saver(Allocator) {} + + std::error_code + parseSwiftExplicitModuleMap(llvm::StringRef fileName, + llvm::StringMap &moduleMap) { + using namespace llvm::yaml; + // Load the input file. + llvm::ErrorOr> fileBufOrErr = + llvm::MemoryBuffer::getFile(fileName); + if (!fileBufOrErr) { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + StringRef Buffer = fileBufOrErr->get()->getBuffer(); + // Use a new source manager instead of the one from ASTContext because we + // don't want the JSON file to be persistent. + llvm::SourceMgr SM; + Stream Stream(llvm::MemoryBufferRef(Buffer, fileName), SM); + for (auto DI = Stream.begin(); DI != Stream.end(); ++DI) { + assert(DI != Stream.end() && "Failed to read a document"); + if (auto *MN = dyn_cast_or_null(DI->getRoot())) { + for (auto &entry : *MN) { + if (parseSingleModuleEntry(entry, moduleMap)) { + return std::make_error_code(std::errc::invalid_argument); + } + } + } else { + return std::make_error_code(std::errc::invalid_argument); + } + } + return std::error_code{}; // success + } + +private: + StringRef getScalaNodeText(llvm::yaml::Node *N) { + SmallString<32> Buffer; + return Saver.save(cast(N)->getValue(Buffer)); + } + + bool parseSingleModuleEntry(llvm::yaml::Node &node, + llvm::StringMap &moduleMap) { + using namespace llvm::yaml; + auto *mapNode = dyn_cast(&node); + if (!mapNode) + return true; + StringRef moduleName; + ExplicitModuleInfo result; + for (auto &entry : *mapNode) { + auto key = getScalaNodeText(entry.getKey()); + auto val = getScalaNodeText(entry.getValue()); + if (key == "moduleName") { + moduleName = val; + } else if (key == "modulePath") { + result.modulePath = val.str(); + } else if (key == "docPath") { + result.moduleDocPath = val.str(); + } else if (key == "sourceInfoPath") { + result.moduleSourceInfoPath = val.str(); + } else { + // Being forgiving for future fields. + continue; + } + } + if (moduleName.empty()) + return true; + moduleMap[moduleName] = std::move(result); + return false; + } + + llvm::StringSaver Saver; +}; + struct ModuleInterfaceLoaderOptions { bool remarkOnRebuildFromInterface = false; bool disableInterfaceLock = false; bool disableImplicitSwiftModule = false; + std::string mainExecutablePath; ModuleInterfaceLoaderOptions(const FrontendOptions &Opts): remarkOnRebuildFromInterface(Opts.RemarkOnRebuildFromModuleInterface), disableInterfaceLock(Opts.DisableInterfaceFileLock), - disableImplicitSwiftModule(Opts.DisableImplicitModules) {} + disableImplicitSwiftModule(Opts.DisableImplicitModules), + mainExecutablePath(Opts.MainExecutablePath) {} ModuleInterfaceLoaderOptions() = default; }; @@ -237,8 +339,17 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts); - std::string getUpToDateCompiledModuleForInterface(StringRef moduleName, - StringRef interfacePath) override; + std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) override; + + /// Given a list of potential ready-to-use compiled modules for \p interfacePath, + /// check if any one of them is up-to-date. If so, emit a forwarding module + /// to the candidate binary module to \p outPath. + bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) override; }; struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { @@ -248,7 +359,7 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { llvm::BumpPtrAllocator Allocator; llvm::StringSaver ArgSaver; std::vector GenericArgs; - CompilerInvocation subInvocation; + CompilerInvocation genericSubInvocation; template InFlightDiagnostic diagnose(StringRef interfacePath, @@ -264,7 +375,8 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { } void inheritOptionsForBuildingInterface(const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts); - bool extractSwiftInterfaceVersionAndArgs(SmallVectorImpl &SubArgs, + bool extractSwiftInterfaceVersionAndArgs(CompilerInvocation &subInvocation, + SmallVectorImpl &SubArgs, std::string &CompilerVersion, StringRef interfacePath, SourceLoc diagnosticLoc); @@ -284,7 +396,7 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { StringRef interfacePath, StringRef outputPath, SourceLoc diagLoc, - llvm::function_ref, + llvm::function_ref, ArrayRef, StringRef)> action) override; bool runInSubCompilerInstance(StringRef moduleName, StringRef interfacePath, diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 4dfff6a0f7d6d..df576eb26d19a 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -33,7 +33,7 @@ struct ModuleInterfaceOptions { bool PreserveTypesAsWritten = false; /// Should we emit the cType when printing @convention(c) or no? - /// FIXME: [clang-function-type-serialization] This check should go away. + /// [TODO: Clang-type-plumbing] This check should go away. bool PrintFullConvention = false; /// Copy of all the command-line flags passed at .swiftinterface @@ -43,6 +43,10 @@ struct ModuleInterfaceOptions { // Print SPI decls and attributes. bool PrintSPIs = false; + + /// Print imports with both @_implementationOnly and @_spi, only applies + /// when PrintSPIs is true. + bool ExperimentalSPIImports = false; }; extern version::Version InterfaceFormatVersion; diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 6fa05dececa47..f847b188f1b42 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -280,8 +280,6 @@ class NameMatcher: public ASTWalker { bool walkToDeclPost(Decl *D) override; std::pair walkToStmtPre(Stmt *S) override; Stmt* walkToStmtPost(Stmt *S) override; - bool walkToTypeLocPre(TypeLoc &TL) override; - bool walkToTypeLocPost(TypeLoc &TL) override; bool walkToTypeReprPre(TypeRepr *T) override; bool walkToTypeReprPost(TypeRepr *T) override; std::pair walkToPatternPre(Pattern *P) override; diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 66f5e7f90bad4..3232e0e6881e9 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -393,6 +393,16 @@ class LinkEntity { /// An access function for prespecialized type metadata. /// The pointer is a canonical TypeBase*. CanonicalSpecializedGenericTypeMetadataAccessFunction, + + /// Metadata for a specialized generic type which cannot be statically + /// guaranteed to be canonical and so must be canonicalized. + /// The pointer is a canonical TypeBase*. + NoncanonicalSpecializedGenericTypeMetadata, + + /// A cache variable for noncanonical specialized type metadata, to be + /// passed to swift_getCanonicalSpecializedMetadata. + /// The pointer is a canonical TypeBase*. + NoncanonicalSpecializedGenericTypeMetadataCacheVariable, }; friend struct llvm::DenseMapInfo; @@ -565,7 +575,7 @@ class LinkEntity { static bool isValidResilientMethodRef(SILDeclRef declRef) { if (declRef.isForeign) return false; - + auto *decl = declRef.getDecl(); return (isa(decl->getDeclContext()) || isa(decl->getDeclContext())); @@ -1045,6 +1055,24 @@ class LinkEntity { return entity; } + static LinkEntity + forNoncanonicalSpecializedGenericTypeMetadata(CanType theType) { + LinkEntity entity; + entity.setForType(Kind::NoncanonicalSpecializedGenericTypeMetadata, + theType); + entity.Data |= LINKENTITY_SET_FIELD( + MetadataAddress, unsigned(TypeMetadataAddress::FullMetadata)); + return entity; + } + + + static LinkEntity + forNoncanonicalSpecializedGenericTypeMetadataCacheVariable(CanType theType) { + LinkEntity entity; + entity.setForType(Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable, theType); + return entity; + } + void mangle(llvm::raw_ostream &out) const; void mangle(SmallVectorImpl &buffer) const; std::string mangleAsString() const; @@ -1152,6 +1180,7 @@ class LinkEntity { } TypeMetadataAddress getMetadataAddress() const { assert(getKind() == Kind::TypeMetadata || + getKind() == Kind::NoncanonicalSpecializedGenericTypeMetadata || getKind() == Kind::ObjCResilientClassStub); return (TypeMetadataAddress)LINKENTITY_GET_FIELD(Data, MetadataAddress); } diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index d465a0f1dfb71..5bd693d2ffe7d 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -224,6 +224,10 @@ def swift_module_file def explict_swift_module_map : Separate<["-"], "explicit-swift-module-map-file">, MetaVarName<"">, HelpText<"Specify a JSON file containing information of explict Swift modules">; + +def placeholder_dependency_module_map + : Separate<["-"], "placeholder-dependency-module-map-file">, MetaVarName<"">, + HelpText<"Specify a JSON file containing information of external Swift module dependencies">; } @@ -259,8 +263,6 @@ def build_request_dependency_graph : Flag<["-"], "build-request-dependency-graph def output_request_graphviz : Separate<["-"], "output-request-graphviz">, 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">; def debug_time_function_bodies : Flag<["-"], "debug-time-function-bodies">, HelpText<"Dumps the time it takes to type-check each function body">; def debug_time_expression_type_checking : Flag<["-"], "debug-time-expression-type-checking">, @@ -327,6 +329,9 @@ def disable_llvm_verify : Flag<["-"], "disable-llvm-verify">, def disable_llvm_value_names : Flag<["-"], "disable-llvm-value-names">, HelpText<"Don't add names to local values in LLVM IR">; +def dump_jit : JoinedOrSeparate<["-"], "dump-jit">, + HelpText<"Dump JIT contents">; + def enable_llvm_value_names : Flag<["-"], "enable-llvm-value-names">, HelpText<"Add names to local values in LLVM IR">; @@ -383,6 +388,10 @@ def enable_experimental_static_assert : Flag<["-"], "enable-experimental-static-assert">, HelpText<"Enable experimental #assert">; +def enable_experimental_concurrency : + Flag<["-"], "enable-experimental-concurrency">, + HelpText<"Enable experimental concurrency model">; + def enable_subst_sil_function_types_for_function_values : Flag<["-"], "enable-subst-sil-function-types-for-function-values">, HelpText<"Use substituted function types for SIL type lowering of function values">; @@ -457,6 +466,9 @@ def warn_long_expression_type_checking_EQ : Joined<["-"], "warn-long-expression- def Rmodule_interface_rebuild : Flag<["-"], "Rmodule-interface-rebuild">, HelpText<"Emits a remark if an imported module needs to be re-compiled from its module interface">; +def enable_volatile_modules : Flag<["-"], "enable-volatile-modules">, + HelpText<"Load Swift modules in memory">; + def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">; def solver_disable_shrink : @@ -634,6 +646,10 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; +def experimental_spi_imports : + Flag<["-"], "experimental-spi-imports">, + HelpText<"Enable experimental support for SPI imports">; + def experimental_print_full_convention : Flag<["-"], "experimental-print-full-convention">, HelpText<"When emitting a module interface, emit additional @convention " @@ -721,4 +737,12 @@ def target_variant_sdk_version : Separate<["-"], "target-variant-sdk-version">, def extra_clang_options_only : Flag<["-"], "only-use-extra-clang-opts">, HelpText<"Options passed via -Xcc are sufficient for Clang configuration">; + +def candidate_module_file + : Separate<["-"], "candidate-module-file">, MetaVarName<"">, + HelpText<"Specify Swift module may be ready to use for an interface">; + +def use_static_resource_dir + : Flag<["-"], "use-static-resource-dir">, + HelpText<"Use resources in the static resource directory">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 4918ef188c961..01017c43315eb 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -197,7 +197,7 @@ HelpText<"Include within-file dependencies.">; def emit_fine_grained_dependency_sourcefile_dot_files : Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, -InternalDebugOpt, +Flags<[FrontendOption, HelpHidden]>, HelpText<"Emit dot files for every source file.">; def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>, @@ -543,9 +543,19 @@ def enable_experimental_additive_arithmetic_derivation : def enable_experimental_concise_pound_file : Flag<["-"], "enable-experimental-concise-pound-file">, - Flags<[FrontendOption]>, + Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Enable experimental concise '#file' identifier">; +def disable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"], + "disable-fuzzy-forward-scan-trailing-closure-matching">, + Flags<[FrontendOption]>, + HelpText<"Disable fuzzy forward-scan trailing closure matching">; + +def enable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"], + "enable-fuzzy-forward-scan-trailing-closure-matching">, + Flags<[FrontendOption]>, + HelpText<"Enable fuzzy forward-scan trailing closure matching">; + def enable_direct_intramodule_dependencies : Flag<["-"], "enable-direct-intramodule-dependencies">, Flags<[FrontendOption, HelpHidden]>, diff --git a/include/swift/Parse/Confusables.def b/include/swift/Parse/Confusables.def index 2423d87eb4e83..b6de929143507 100644 --- a/include/swift/Parse/Confusables.def +++ b/include/swift/Parse/Confusables.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2020 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 @@ -10,118 +10,127 @@ // //===----------------------------------------------------------------------===// -// CONFUSABLE(CONFUSABLE_POINT, BASEPOINT) +//////////////////////////////////////////////////////////////////////////////// +// WARNING: This file is manually generated from +// utils/UnicodeData/confusables.txt and should not be directly modified. +// Run utils/generate_confusables.py to regenerate this file. +//////////////////////////////////////////////////////////////////////////////// -CONFUSABLE(0x2010, 0x2d) -CONFUSABLE(0x2011, 0x2d) -CONFUSABLE(0x2012, 0x2d) -CONFUSABLE(0x2013, 0x2d) -CONFUSABLE(0xfe58, 0x2d) -CONFUSABLE(0x6d4, 0x2d) -CONFUSABLE(0x2043, 0x2d) -CONFUSABLE(0x2d7, 0x2d) -CONFUSABLE(0x2212, 0x2d) -CONFUSABLE(0x2796, 0x2d) -CONFUSABLE(0x2cba, 0x2d) -CONFUSABLE(0x60d, 0x2c) -CONFUSABLE(0x66b, 0x2c) -CONFUSABLE(0x201a, 0x2c) -CONFUSABLE(0xb8, 0x2c) -CONFUSABLE(0xa4f9, 0x2c) -CONFUSABLE(0x903, 0x3a) -CONFUSABLE(0xa83, 0x3a) -CONFUSABLE(0xff1a, 0x3a) -CONFUSABLE(0x589, 0x3a) -CONFUSABLE(0x703, 0x3a) -CONFUSABLE(0x704, 0x3a) -CONFUSABLE(0x16ec, 0x3a) -CONFUSABLE(0xfe30, 0x3a) -CONFUSABLE(0x1803, 0x3a) -CONFUSABLE(0x1809, 0x3a) -CONFUSABLE(0x205a, 0x3a) -CONFUSABLE(0x5c3, 0x3a) -CONFUSABLE(0x2f8, 0x3a) -CONFUSABLE(0xa789, 0x3a) -CONFUSABLE(0x2236, 0x3a) -CONFUSABLE(0x2d0, 0x3a) -CONFUSABLE(0xa4fd, 0x3a) -CONFUSABLE(0xff01, 0x21) -CONFUSABLE(0x1c3, 0x21) -CONFUSABLE(0x2d51, 0x21) -CONFUSABLE(0x294, 0x3f) -CONFUSABLE(0x241, 0x3f) -CONFUSABLE(0x97d, 0x3f) -CONFUSABLE(0x13ae, 0x3f) -CONFUSABLE(0xa6eb, 0x3f) -CONFUSABLE(0x1d16d, 0x2e) -CONFUSABLE(0x2024, 0x2e) -CONFUSABLE(0x701, 0x2e) -CONFUSABLE(0x702, 0x2e) -CONFUSABLE(0xa60e, 0x2e) -CONFUSABLE(0x10a50, 0x2e) -CONFUSABLE(0x660, 0x2e) -CONFUSABLE(0x6f0, 0x2e) -CONFUSABLE(0xa4f8, 0x2e) -CONFUSABLE(0xff3b, 0x28) -CONFUSABLE(0x2768, 0x28) -CONFUSABLE(0x2772, 0x28) -CONFUSABLE(0x3014, 0x28) -CONFUSABLE(0xfd3e, 0x28) -CONFUSABLE(0xff3d, 0x29) -CONFUSABLE(0x2769, 0x29) -CONFUSABLE(0x2773, 0x29) -CONFUSABLE(0x3015, 0x29) -CONFUSABLE(0xfd3f, 0x29) -CONFUSABLE(0x2774, 0x7b) -CONFUSABLE(0x1d114, 0x7b) -CONFUSABLE(0x2775, 0x7d) -CONFUSABLE(0x204e, 0x2a) -CONFUSABLE(0x66d, 0x2a) -CONFUSABLE(0x2217, 0x2a) -CONFUSABLE(0x1031f, 0x2a) -CONFUSABLE(0x1735, 0x2f) -CONFUSABLE(0x2041, 0x2f) -CONFUSABLE(0x2215, 0x2f) -CONFUSABLE(0x2044, 0x2f) -CONFUSABLE(0x2571, 0x2f) -CONFUSABLE(0x27cb, 0x2f) -CONFUSABLE(0x29f8, 0x2f) -CONFUSABLE(0x1d23a, 0x2f) -CONFUSABLE(0x31d3, 0x2f) -CONFUSABLE(0x3033, 0x2f) -CONFUSABLE(0x2cc6, 0x2f) -CONFUSABLE(0x30ce, 0x2f) -CONFUSABLE(0x4e3f, 0x2f) -CONFUSABLE(0x2f03, 0x2f) -CONFUSABLE(0xff3c, 0x5c) -CONFUSABLE(0xfe68, 0x5c) -CONFUSABLE(0x2216, 0x5c) -CONFUSABLE(0x27cd, 0x5c) -CONFUSABLE(0x29f5, 0x5c) -CONFUSABLE(0x29f9, 0x5c) -CONFUSABLE(0x1d20f, 0x5c) -CONFUSABLE(0x1d23b, 0x5c) -CONFUSABLE(0x31d4, 0x5c) -CONFUSABLE(0x4e36, 0x5c) -CONFUSABLE(0x2f02, 0x5c) -CONFUSABLE(0xa778, 0x26) -CONFUSABLE(0x16ed, 0x2b) -CONFUSABLE(0x2795, 0x2b) -CONFUSABLE(0x1029b, 0x2b) -CONFUSABLE(0x2039, 0x3c) -CONFUSABLE(0x276e, 0x3c) -CONFUSABLE(0x2c2, 0x3c) -CONFUSABLE(0x1d236, 0x3c) -CONFUSABLE(0x1438, 0x3c) -CONFUSABLE(0x16b2, 0x3c) -CONFUSABLE(0x1400, 0x3d) -CONFUSABLE(0x2e40, 0x3d) -CONFUSABLE(0x30a0, 0x3d) -CONFUSABLE(0xa4ff, 0x3d) -CONFUSABLE(0x203a, 0x3e) -CONFUSABLE(0x276f, 0x3e) -CONFUSABLE(0x2c3, 0x3e) -CONFUSABLE(0x1d237, 0x3e) -CONFUSABLE(0x1433, 0x3e) + +// CONFUSABLE(CONFUSABLE_POINT, CONFUSABLE_NAME, BASE_POINT, BASE_NAME) + +CONFUSABLE(0x2010, "Hyphen", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2011, "Non-Breaking Hyphen", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2012, "Figure Dash", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2013, "En Dash", 0x2d, "Hyphen Minus") +CONFUSABLE(0xfe58, "Small Em Dash", 0x2d, "Hyphen Minus") +CONFUSABLE(0x6d4, "Arabic Full Stop", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2043, "Hyphen Bullet", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2d7, "Modifier Letter Minus Sign", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2212, "Minus Sign", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2796, "Heavy Minus Sign", 0x2d, "Hyphen Minus") +CONFUSABLE(0x2cba, "Coptic Capital Letter Dialect-P Ni", 0x2d, "Hyphen Minus") +CONFUSABLE(0x60d, "Arabic Date Separator", 0x2c, "Comma") +CONFUSABLE(0x66b, "Arabic Decimal Separator", 0x2c, "Comma") +CONFUSABLE(0x201a, "Single Low-9 Quotation Mark", 0x2c, "Comma") +CONFUSABLE(0xb8, "Cedilla", 0x2c, "Comma") +CONFUSABLE(0xa4f9, "Lisu Letter Tone Na Po", 0x2c, "Comma") +CONFUSABLE(0x37e, "Greek Question Mark", 0x3b, "Semicolon") +CONFUSABLE(0x903, "Devanagari Sign Visarga", 0x3a, "Colon") +CONFUSABLE(0xa83, "Gujarati Sign Visarga", 0x3a, "Colon") +CONFUSABLE(0xff1a, "Fullwidth Colon", 0x3a, "Colon") +CONFUSABLE(0x589, "Armenian Full Stop", 0x3a, "Colon") +CONFUSABLE(0x703, "Syriac Supralinear Colon", 0x3a, "Colon") +CONFUSABLE(0x704, "Syriac Sublinear Colon", 0x3a, "Colon") +CONFUSABLE(0x16ec, "Runic Multiple Punctuation", 0x3a, "Colon") +CONFUSABLE(0xfe30, "Presentation Form For Vertical Two Dot Leader", 0x3a, "Colon") +CONFUSABLE(0x1803, "Mongolian Full Stop", 0x3a, "Colon") +CONFUSABLE(0x1809, "Mongolian Manchu Full Stop", 0x3a, "Colon") +CONFUSABLE(0x205a, "Two Dot Punctuation", 0x3a, "Colon") +CONFUSABLE(0x5c3, "Hebrew Punctuation Sof Pasuq", 0x3a, "Colon") +CONFUSABLE(0x2f8, "Modifier Letter Raised Colon", 0x3a, "Colon") +CONFUSABLE(0xa789, "Modifier Letter Colon", 0x3a, "Colon") +CONFUSABLE(0x2236, "Ratio", 0x3a, "Colon") +CONFUSABLE(0x2d0, "Modifier Letter Triangular Colon", 0x3a, "Colon") +CONFUSABLE(0xa4fd, "Lisu Letter Tone Mya Jeu", 0x3a, "Colon") +CONFUSABLE(0xff01, "Fullwidth Exclamation Mark", 0x21, "Exclamation Mark") +CONFUSABLE(0x1c3, "Latin Letter Retroflex Click", 0x21, "Exclamation Mark") +CONFUSABLE(0x2d51, "Tifinagh Letter Tuareg Yang", 0x21, "Exclamation Mark") +CONFUSABLE(0x294, "Latin Letter Glottal Stop", 0x3f, "Question Mark") +CONFUSABLE(0x241, "Latin Capital Letter Glottal Stop", 0x3f, "Question Mark") +CONFUSABLE(0x97d, "Devanagari Letter Glottal Stop", 0x3f, "Question Mark") +CONFUSABLE(0x13ae, "Cherokee Letter He", 0x3f, "Question Mark") +CONFUSABLE(0xa6eb, "Bamum Letter Ntuu", 0x3f, "Question Mark") +CONFUSABLE(0x1d16d, "Musical Symbol Combining Augmentation Dot", 0x2e, "Full Stop") +CONFUSABLE(0x2024, "One Dot Leader", 0x2e, "Full Stop") +CONFUSABLE(0x701, "Syriac Supralinear Full Stop", 0x2e, "Full Stop") +CONFUSABLE(0x702, "Syriac Sublinear Full Stop", 0x2e, "Full Stop") +CONFUSABLE(0xa60e, "Vai Full Stop", 0x2e, "Full Stop") +CONFUSABLE(0x10a50, "Kharoshthi Punctuation Dot", 0x2e, "Full Stop") +CONFUSABLE(0x660, "Arabic-Indic Digit Zero", 0x2e, "Full Stop") +CONFUSABLE(0x6f0, "Extended Arabic-Indic Digit Zero", 0x2e, "Full Stop") +CONFUSABLE(0xa4f8, "Lisu Letter Tone Mya Ti", 0x2e, "Full Stop") +CONFUSABLE(0xff3b, "Fullwidth Left Square Bracket", 0x28, "Left Parenthesis") +CONFUSABLE(0x2768, "Medium Left Parenthesis Ornament", 0x28, "Left Parenthesis") +CONFUSABLE(0x2772, "Light Left Tortoise Shell Bracket Ornament", 0x28, "Left Parenthesis") +CONFUSABLE(0x3014, "Left Tortoise Shell Bracket", 0x28, "Left Parenthesis") +CONFUSABLE(0xfd3e, "Ornate Left Parenthesis", 0x28, "Left Parenthesis") +CONFUSABLE(0xff3d, "Fullwidth Right Square Bracket", 0x29, "Right Parenthesis") +CONFUSABLE(0x2769, "Medium Right Parenthesis Ornament", 0x29, "Right Parenthesis") +CONFUSABLE(0x2773, "Light Right Tortoise Shell Bracket Ornament", 0x29, "Right Parenthesis") +CONFUSABLE(0x3015, "Right Tortoise Shell Bracket", 0x29, "Right Parenthesis") +CONFUSABLE(0xfd3f, "Ornate Right Parenthesis", 0x29, "Right Parenthesis") +CONFUSABLE(0x2774, "Medium Left Curly Bracket Ornament", 0x7b, "Left Curly Bracket") +CONFUSABLE(0x1d114, "Musical Symbol Brace", 0x7b, "Left Curly Bracket") +CONFUSABLE(0x2775, "Medium Right Curly Bracket Ornament", 0x7d, "Right Curly Bracket") +CONFUSABLE(0x204e, "Low Asterisk", 0x2a, "Asterisk") +CONFUSABLE(0x66d, "Arabic Five Pointed Star", 0x2a, "Asterisk") +CONFUSABLE(0x2217, "Asterisk Operator", 0x2a, "Asterisk") +CONFUSABLE(0x1031f, "Old Italic Letter Ess", 0x2a, "Asterisk") +CONFUSABLE(0x1735, "Philippine Single Punctuation", 0x2f, "Forward Slash") +CONFUSABLE(0x2041, "Caret Insertion Point", 0x2f, "Forward Slash") +CONFUSABLE(0x2215, "Division Slash", 0x2f, "Forward Slash") +CONFUSABLE(0x2044, "Fraction Slash", 0x2f, "Forward Slash") +CONFUSABLE(0x2571, "Box Drawings Light Diagonal Upper Right To Lower Left", 0x2f, "Forward Slash") +CONFUSABLE(0x27cb, "Mathematical Rising Diagonal", 0x2f, "Forward Slash") +CONFUSABLE(0x29f8, "Big Solidus", 0x2f, "Forward Slash") +CONFUSABLE(0x1d23a, "Greek Instrumental Notation Symbol-47", 0x2f, "Forward Slash") +CONFUSABLE(0x31d3, "Cjk Stroke Sp", 0x2f, "Forward Slash") +CONFUSABLE(0x3033, "Vertical Kana Repeat Mark Upper Half", 0x2f, "Forward Slash") +CONFUSABLE(0x2cc6, "Coptic Capital Letter Old Coptic Esh", 0x2f, "Forward Slash") +CONFUSABLE(0x30ce, "Katakana Letter No", 0x2f, "Forward Slash") +CONFUSABLE(0x4e3f, "Cjk Unified Ideograph-4E3F", 0x2f, "Forward Slash") +CONFUSABLE(0x2f03, "Kangxi Radical Slash", 0x2f, "Forward Slash") +CONFUSABLE(0xff3c, "Fullwidth Reverse Solidus", 0x5c, "Back Slash") +CONFUSABLE(0xfe68, "Small Reverse Solidus", 0x5c, "Back Slash") +CONFUSABLE(0x2216, "Set Minus", 0x5c, "Back Slash") +CONFUSABLE(0x27cd, "Mathematical Falling Diagonal", 0x5c, "Back Slash") +CONFUSABLE(0x29f5, "Reverse Solidus Operator", 0x5c, "Back Slash") +CONFUSABLE(0x29f9, "Big Reverse Solidus", 0x5c, "Back Slash") +CONFUSABLE(0x1d20f, "Greek Vocal Notation Symbol-16", 0x5c, "Back Slash") +CONFUSABLE(0x1d23b, "Greek Instrumental Notation Symbol-48", 0x5c, "Back Slash") +CONFUSABLE(0x31d4, "Cjk Stroke D", 0x5c, "Back Slash") +CONFUSABLE(0x4e36, "Cjk Unified Ideograph-4E36", 0x5c, "Back Slash") +CONFUSABLE(0x2f02, "Kangxi Radical Dot", 0x5c, "Back Slash") +CONFUSABLE(0xa778, "Latin Small Letter Um", 0x26, "Ampersand") +CONFUSABLE(0x16ed, "Runic Cross Punctuation", 0x2b, "Plus Sign") +CONFUSABLE(0x2795, "Heavy Plus Sign", 0x2b, "Plus Sign") +CONFUSABLE(0x1029b, "Lycian Letter H", 0x2b, "Plus Sign") +CONFUSABLE(0x2039, "Single Left-Pointing Angle Quotation Mark", 0x3c, "Less Than Sign") +CONFUSABLE(0x276e, "Heavy Left-Pointing Angle Quotation Mark Ornament", 0x3c, "Less Than Sign") +CONFUSABLE(0x2c2, "Modifier Letter Left Arrowhead", 0x3c, "Less Than Sign") +CONFUSABLE(0x1d236, "Greek Instrumental Notation Symbol-40", 0x3c, "Less Than Sign") +CONFUSABLE(0x1438, "Canadian Syllabics Pa", 0x3c, "Less Than Sign") +CONFUSABLE(0x16b2, "Runic Letter Kauna", 0x3c, "Less Than Sign") +CONFUSABLE(0x1400, "Canadian Syllabics Hyphen", 0x3d, "Equals Sign") +CONFUSABLE(0x2e40, "Double Hyphen", 0x3d, "Equals Sign") +CONFUSABLE(0x30a0, "Katakana-Hiragana Double Hyphen", 0x3d, "Equals Sign") +CONFUSABLE(0xa4ff, "Lisu Punctuation Full Stop", 0x3d, "Equals Sign") +CONFUSABLE(0x203a, "Single Right-Pointing Angle Quotation Mark", 0x3e, "Greater Than Sign") +CONFUSABLE(0x276f, "Heavy Right-Pointing Angle Quotation Mark Ornament", 0x3e, "Greater Than Sign") +CONFUSABLE(0x2c3, "Modifier Letter Right Arrowhead", 0x3e, "Greater Than Sign") +CONFUSABLE(0x1d237, "Greek Instrumental Notation Symbol-42", 0x3e, "Greater Than Sign") +CONFUSABLE(0x1433, "Canadian Syllabics Po", 0x3e, "Greater Than Sign") +CONFUSABLE(0x16f3f, "Miao Letter Archaic Zza", 0x3e, "Greater Than Sign") #undef CONFUSABLE diff --git a/include/swift/Parse/Confusables.h b/include/swift/Parse/Confusables.h index fe8f59cf652e7..01a7c761e1662 100644 --- a/include/swift/Parse/Confusables.h +++ b/include/swift/Parse/Confusables.h @@ -13,6 +13,7 @@ #ifndef SWIFT_CONFUSABLES_H #define SWIFT_CONFUSABLES_H +#include "llvm/ADT/StringRef.h" #include namespace swift { @@ -21,6 +22,12 @@ namespace confusable { /// specification table of confusable characters and maps to punctuation, /// and either returns either the expected ASCII character or 0. char tryConvertConfusableCharacterToASCII(uint32_t codepoint); + + /// Given a UTF-8 codepoint which is previously determined to be confusable, + /// return the name of the confusable character and the name of the base + /// character. + std::pair + getConfusableAndBaseCodepointNames(uint32_t codepoint); } } diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 75d710cc6769b..625092e4a3ba3 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -660,7 +660,7 @@ class Parser { /// \returns true if there is an instance of \c T1 on the current line (this /// avoids the foot-gun of not considering T1 starting the next line for a /// plain Tok.is(T1) check). - bool skipUntilTokenOrEndOfLine(tok T1); + bool skipUntilTokenOrEndOfLine(tok T1, tok T2 = tok::NUM_TOKENS); /// Skip a braced block (e.g. function body). The current token must be '{'. /// Returns \c true if the parser hit the eof before finding matched '}'. @@ -1351,10 +1351,24 @@ class Parser { DeclName &fullName, ParameterList *&bodyParams, DefaultArgumentInfo &defaultArgs, + SourceLoc &asyncLoc, SourceLoc &throws, bool &rethrows, TypeRepr *&retType); + /// Parse 'async' and 'throws', if present, putting the locations of the + /// keywords into the \c SourceLoc parameters. + /// + /// \param existingArrowLoc The location of an existing '->', if there is + /// one. Parsing 'async' or 'throws' after the `->` is an error we + /// correct for. + /// + /// \param rethrows If non-NULL, will also parse the 'rethrows' keyword in + /// lieu of 'throws'. + void parseAsyncThrows( + SourceLoc existingArrowLoc, SourceLoc &asyncLoc, SourceLoc &throwsLoc, + bool *rethrows); + //===--------------------------------------------------------------------===// // Pattern Parsing @@ -1647,9 +1661,7 @@ class Parser { diagnoseWhereClauseInGenericParamList(const GenericParamList *GenericParams); ParserStatus - parseFreestandingGenericWhereClause(GenericContext *genCtx, - GenericParamList *&GPList, - ParseDeclOptions flags); + parseFreestandingGenericWhereClause(GenericContext *genCtx); ParserStatus parseGenericWhereClause( SourceLoc &WhereLoc, SmallVectorImpl &Requirements, diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index 610507ce6577c..08915aa090b73 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -38,6 +38,8 @@ #include #include +#include + namespace { template struct MachOTraits; @@ -848,6 +850,36 @@ class ReflectionContext } } + llvm::Optional> + metadataAllocationCacheNode(MetadataAllocation Allocation) { + switch (Allocation.Tag) { + case BoxesTag: + case ObjCClassWrappersTag: + case FunctionTypesTag: + case MetatypeTypesTag: + case ExistentialMetatypeValueWitnessTablesTag: + case ExistentialMetatypesTag: + case ExistentialTypesTag: + case OpaqueExistentialValueWitnessTablesTag: + case ClassExistentialValueWitnessTablesTag: + case ForeignWitnessTablesTag: + case TupleCacheTag: + case GenericMetadataCacheTag: + case ForeignMetadataCacheTag: + case GenericWitnessTableCacheTag: { + auto NodeBytes = getReader().readBytes( + RemoteAddress(Allocation.Ptr), sizeof(MetadataCacheNode)); + auto Node = + reinterpret_cast *>(NodeBytes.get()); + if (!Node) + return llvm::None; + return *Node; + } + default: + return llvm::None; + } + } + /// Iterate the metadata allocations in the target process, calling Call with /// each allocation found. Returns None on success, and a string describing /// the error on failure. @@ -958,7 +990,8 @@ class ReflectionContext // FIXME: std::stringstream would be better, but LLVM's standard library // introduces a vtable and we don't want that. char result[128]; - std::snprintf(result, sizeof(result), "unable to read Next pointer %p", + std::snprintf(result, sizeof(result), + "unable to read Next pointer %#" PRIx64, BacktraceListNext.getAddressData()); return std::string(result); } diff --git a/include/swift/Reflection/RuntimeInternals.h b/include/swift/Reflection/RuntimeInternals.h index 0fa056360e9c6..11b6027242aaf 100644 --- a/include/swift/Reflection/RuntimeInternals.h +++ b/include/swift/Reflection/RuntimeInternals.h @@ -41,6 +41,11 @@ struct MetadataAllocation { unsigned Size; }; +template struct MetadataCacheNode { + typename Runtime::StoredPointer Left; + typename Runtime::StoredPointer Right; +}; + } // end namespace reflection } // end namespace swift diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index 505a1acbc6aba..2637f616ae007 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -302,38 +302,28 @@ class BoundGenericTypeRef final : public TypeRef, public NominalTypeTrait { class TupleTypeRef final : public TypeRef { std::vector Elements; - bool Variadic; - static TypeRefID Profile(const std::vector &Elements, - bool Variadic) { + static TypeRefID Profile(const std::vector &Elements) { TypeRefID ID; for (auto Element : Elements) ID.addPointer(Element); - - ID.addInteger(static_cast(Variadic)); return ID; } public: - TupleTypeRef(std::vector Elements, bool Variadic=false) - : TypeRef(TypeRefKind::Tuple), Elements(std::move(Elements)), - Variadic(Variadic) {} + TupleTypeRef(std::vector Elements) + : TypeRef(TypeRefKind::Tuple), Elements(std::move(Elements)) {} template static const TupleTypeRef *create(Allocator &A, - std::vector Elements, - bool Variadic = false) { - FIND_OR_CREATE_TYPEREF(A, TupleTypeRef, Elements, Variadic); + std::vector Elements) { + FIND_OR_CREATE_TYPEREF(A, TupleTypeRef, Elements); } const std::vector &getElements() const { return Elements; }; - bool isVariadic() const { - return Variadic; - } - static bool classof(const TypeRef *TR) { return TR->getKind() == TypeRefKind::Tuple; } diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 9d2b743f1be1d..f375918a5ff5d 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -404,10 +404,10 @@ class TypeRefBuilder { } const TupleTypeRef *createTupleType(llvm::ArrayRef elements, - std::string &&labels, bool isVariadic) { + std::string &&labels) { // FIXME: Add uniqueness checks in TupleTypeRef::Profile and // unittests/Reflection/TypeRef.cpp if using labels for identity. - return TupleTypeRef::create(*this, elements, isVariadic); + return TupleTypeRef::create(*this, elements); } const FunctionTypeRef *createFunctionType( @@ -445,7 +445,7 @@ class TypeRefBuilder { break; } - auto result = createTupleType({}, "", false); + auto result = createTupleType({}, ""); return FunctionTypeRef::create(*this, {}, result, funcFlags); } diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 963bcf6386691..4450066283afc 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -509,6 +509,7 @@ class MetadataReader { if (!meta || meta->getKind() != MetadataKind::Class) return None; +#if SWIFT_OBJC_INTEROP // The following algorithm only works on the non-fragile Apple runtime. // Grab the RO-data pointer. This part is not ABI. @@ -524,6 +525,23 @@ class MetadataReader { return None; return start; +#else + // All swift class instances start with an isa pointer, + // followed by the retain counts (which are the size of a long long). + size_t isaAndRetainCountSize = sizeof(StoredSize) + sizeof(long long); + size_t start = isaAndRetainCountSize; + + auto classMeta = cast>(meta); + while (classMeta->Superclass) { + classMeta = cast>( + readMetadata(classMeta->Superclass)); + + // Subtract the size contribution of the isa and retain counts from + // the super class. + start += classMeta->InstanceSize - isaAndRetainCountSize; + } + return start; +#endif } /// Given a pointer to the metadata, attempt to read the value @@ -759,8 +777,8 @@ class MetadataReader { !Reader->readString(RemoteAddress(tupleMeta->Labels), labels)) return BuiltType(); - auto BuiltTuple = Builder.createTupleType(elementTypes, std::move(labels), - /*variadic*/ false); + auto BuiltTuple = + Builder.createTupleType(elementTypes, std::move(labels)); TypeCache[MetadataAddress] = BuiltTuple; return BuiltTuple; } @@ -785,7 +803,8 @@ class MetadataReader { auto flags = FunctionTypeFlags() .withConvention(Function->getConvention()) - .withThrows(Function->throws()) + .withAsync(Function->isAsync()) + .withThrows(Function->isThrowing()) .withParameterFlags(Function->hasParameterFlags()) .withEscaping(Function->isEscaping()); auto BuiltFunction = diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 4ad4b8751ab31..8d5537ed2e94b 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -320,6 +320,26 @@ MetadataResponse swift_getSingletonMetadata(MetadataRequest request, const TypeContextDescriptor *description); +/// Fetch a uniqued metadata object for the generic nominal type described by +/// the provided candidate metadata, using that candidate metadata if there is +/// not already a canonical metadata. +/// +/// Runtime availability: Swift 5.4 +/// +/// \param candidate A prespecialized metadata record for a type which is not +/// statically made to be canonical which will be canonicalized +/// if no other canonical metadata exists for the type. +/// \param cache A pointer to a cache which will be set to the canonical +/// metadata record for the type described by the candidate +/// metadata record. If the cache has already been populated, its +/// contents will be returned. +/// \returns The canonical metadata for the specialized generic type described +/// by the provided candidate metadata. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) MetadataResponse + swift_getCanonicalSpecializedMetadata(MetadataRequest request, + const Metadata *candidate, + const Metadata **cache); + /// Fetch a uniqued metadata object for a generic nominal type. SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) MetadataResponse diff --git a/include/swift/Runtime/ObjCBridge.h b/include/swift/Runtime/ObjCBridge.h index 33e5022463434..a24fb03feeb9d 100644 --- a/include/swift/Runtime/ObjCBridge.h +++ b/include/swift/Runtime/ObjCBridge.h @@ -68,6 +68,9 @@ OBJC_EXPORT Class objc_readClassPair(Class cls, const struct objc_image_info *info) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); +// Magic symbol whose _address_ is the runtime's isa mask. +OBJC_EXPORT const struct { char c; } objc_absolute_packed_isa_class_mask; + namespace swift { diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 910c1d2788245..5ff38ed39398b 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -645,6 +645,14 @@ FUNCTION(GetGenericMetadata, swift_getGenericMetadata, ARGS(SizeTy, Int8PtrTy, TypeContextDescriptorPtrTy), ATTRS(NoUnwind, ReadOnly)) +// MetadataResponse swift_getCanonicalSpecializedMetadata(MetadataRequest request, +// Metadata *candidate); +FUNCTION(GetCanonicalSpecializedMetadata, swift_getCanonicalSpecializedMetadata, + SwiftCC, GetCanonicalSpecializedMetadataAvailability, + RETURNS(TypeMetadataResponseTy), + ARGS(SizeTy, TypeMetadataPtrTy, TypeMetadataPtrPtrTy), + ATTRS(NoUnwind, ReadOnly)) + // MetadataResponse swift_getOpaqueTypeMetadata(MetadataRequest request, // const void * const *arguments, // const OpaqueTypeDescriptor *descriptor, @@ -1196,7 +1204,7 @@ FUNCTION(ConformsToProtocol, // bool swift_isClassType(type*); FUNCTION(IsClassType, - swift_isClassType, C_CC, AlwaysAvailable, + swift_isClassType, SwiftCC, AlwaysAvailable, RETURNS(Int1Ty), ARGS(TypeMetadataPtrTy), ATTRS(ZExt, NoUnwind, ReadNone)) diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index de97fef3e9bf0..e11ca87b047b6 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -21,6 +21,7 @@ #ifndef SWIFT_SIL_APPLYSITE_H #define SWIFT_SIL_APPLYSITE_H +#include "swift/Basic/STLExtras.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILFunction.h" @@ -29,6 +30,8 @@ namespace swift { +class FullApplySite; + //===----------------------------------------------------------------------===// // ApplySite //===----------------------------------------------------------------------===// @@ -396,6 +399,10 @@ class ApplySite { llvm_unreachable("covered switch"); } + /// Returns true if \p op is an operand that passes an indirect + /// result argument to the apply site. + bool isIndirectResultOperand(const Operand &op) const; + /// Return whether the given apply is of a formally-throwing function /// which is statically known not to throw. bool isNonThrowing() const { @@ -425,6 +432,10 @@ class ApplySite { } void dump() const LLVM_ATTRIBUTE_USED { getInstruction()->dump(); } + + /// Attempt to cast this apply site to a full apply site, returning None on + /// failure. + Optional asFullApplySite() const; }; //===----------------------------------------------------------------------===// @@ -562,12 +573,6 @@ class FullApplySite : public ApplySite { return op.getOperandNumber() < getOperandIndexOfFirstArgument(); } - /// Returns true if \p op is an operand that passes an indirect - /// result argument to the apply site. - bool isIndirectResultOperand(const Operand &op) const { - return getCalleeArgIndex(op) < getNumIndirectSILResults(); - } - /// Is this an ApplySite that begins the evaluation of a coroutine. bool beginsCoroutineEvaluation() const { switch (getKind()) { @@ -637,6 +642,12 @@ class FullApplySite : public ApplySite { llvm_unreachable("covered switch isn't covered"); } + /// Returns true if \p op is an operand that passes an indirect + /// result argument to the apply site. + bool isIndirectResultOperand(const Operand &op) const { + return getCalleeArgIndex(op) < getNumIndirectSILResults(); + } + static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); } static bool classof(const SILInstruction *inst) { @@ -729,4 +740,23 @@ template <> struct DenseMapInfo<::swift::FullApplySite> { } // namespace llvm +//===----------------------------------------------------------------------===// +// Inline Definitions to work around Forward Declaration +//===----------------------------------------------------------------------===// + +namespace swift { + +inline Optional ApplySite::asFullApplySite() const { + return FullApplySite::isa(getInstruction()); +} + +inline bool ApplySite::isIndirectResultOperand(const Operand &op) const { + auto fas = asFullApplySite(); + if (!fas) + return false; + return fas->isIndirectResultOperand(op); +} + +} // namespace swift + #endif diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index af2bda9b68365..0008c565e5246 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -118,6 +118,10 @@ bool mayCheckRefCount(SILInstruction *User); /// run-time sanitizers. bool isSanitizerInstrumentation(SILInstruction *Instruction); +/// Return true when the instruction represents added instrumentation for +/// run-time sanitizers or code coverage. +bool isInstrumentation(SILInstruction *Instruction); + /// Check that this is a partial apply of a reabstraction thunk and return the /// argument of the partial apply if it is. SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 295cf1150fab7..dd198db2b6814 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -10,26 +10,51 @@ // //===----------------------------------------------------------------------===// /// -/// These utilities model formal memory access locations as marked by -/// begin_access and end_access instructions. The formal memory locations -/// identified here must be consistent with language rules for exclusity -/// enforcement. This is not meant to be a utility to reason about other general -/// properties of SIL memory operations such as reference count identity, -/// ownership, or aliasing. Code that queries the properties of arbitrary memory -/// operations independent of begin_access instructions should use a different -/// interface. -/// -/// SIL memory addresses used for formal access need to meet special -/// requirements. In particular, it must be possible to identifiy the storage by -/// following the pointer's provenance. This is *not* true for SIL memory -/// operations in general. The utilities cannot simply bailout on unrecognized -/// patterns. Doing so would lead to undefined program behavior, which isn't -/// something that can be directly tested (i.e. if this breaks, we won't see -/// test failures). -/// -/// These utilities are mainly meant to be used by AccessEnforcement passes, -/// which optimize exclusivity enforcement. They live in SIL so they can be used -/// by SIL verification. +/// These utilities model the storage locations of memory access. +/// +/// All memory operations that are part of a formal access, as defined by +/// exclusivity rules, are marked by begin_access and end_access instructions. +/// +/// Currently, access markers are stripped early in the pipeline. An active +/// goal is to require access markers in OSSA form, and to enable access +/// marker verification. +/// +/// To verify access markers, SIL checks that all memory operations either have +/// an address that originates in begin_access, or originates from a pattern +/// that is recognized as a non-formal-access. This implies that every SIL +/// memory operation has a recognizable address source. +/// +/// If the memory operation is part of a formal access, then getAddressAccess() +/// returns the begin_access marker. +/// +/// AccessedStorage identifies the storage location of a memory access. +/// +/// identifyFormalAccess() returns the formally accessed storage of a +/// begin_access instruction. This must return a valid AccessedStorage value +/// unless the access has "Unsafe" enforcement. The formal access location may +/// be nested within an outer begin_access. For the purpose of exclusivity, +/// nested accesses are considered distinct formal accesses so they return +/// distinct AccessedStorage values even though they may access the same +/// memory. +/// +/// findAccessedStorage() returns the outermost AccessedStorage for any memory +/// address. It can be called on the address of a memory operation, the address +/// of a begin_access, or any other address value. If the address is from an +/// enforced begin_access or from any memory operation that is part of a formal +/// access, then it returns a valid AccessedStorage value. If the memory +/// operation is not part of a formal access, then it still identifies the +/// accessed location as a best effort, but the result may be invalid storage. +/// +/// An active goal is to require findAccessedStorage() to always return a +/// valid AccessedStorage value even for operations that aren't part of a +/// formal access. +/// +/// The AccessEnforcementWMO pass is an example of an optimistic optimization +/// that relies on the above requirements for correctness. If +/// findAccessedStorage() simply bailed out on an unrecognized memory address by +/// returning an invalid AccessedStorage, then the optimization could make +/// incorrect assumptions about the absence of access to globals or class +/// properties. /// //===----------------------------------------------------------------------===// @@ -52,33 +77,52 @@ namespace swift { /// Get the base address of a formal access by stripping access markers. /// -/// If \p v is an address, then the returned value is also an address -/// (pointer-to-address is not stripped). -SILValue stripAccessMarkers(SILValue v); +/// Postcondition: If \p v is an address, then the returned value is also an +/// address (pointer-to-address is not stripped). +inline SILValue stripAccessMarkers(SILValue v) { + while (auto *bai = dyn_cast(v)) { + v = bai->getOperand(); + } + return v; +} -/// Return a non-null address-type SingleValueInstruction if \p v is the result -/// of an address projection that may be inside of a formal access, such as +/// An address projection that may be inside of a formal access, such as /// (begin_borrow, struct_element_addr, tuple_element_addr). -/// -/// The resulting projection must have an address-type operand at index zero -/// representing the projected address. -SingleValueInstruction *isAccessProjection(SILValue v); +struct AccessProjection { + SingleValueInstruction *projectionInst = nullptr; -/// Attempt to return the address corresponding to a variable's formal access -/// by stripping indexing and address projections. -/// -/// \p v must be an address. + /// If \p v is not a recognized access projection the result is invalid. + AccessProjection(SILValue v) { + switch (v->getKind()) { + default: + break; + + case ValueKind::StructElementAddrInst: + case ValueKind::TupleElementAddrInst: + case ValueKind::UncheckedTakeEnumDataAddrInst: + case ValueKind::TailAddrInst: + case ValueKind::IndexAddrInst: + projectionInst = cast(v); + }; + } + + operator bool() const { return projectionInst != nullptr; } + + SILValue baseAddress() const { return projectionInst->getOperand(0); } +}; + +/// Return the base address after stripping access projections. If \p v is an +/// access projection, return the enclosing begin_access. Otherwise, return a +/// "best effort" base address. /// -/// Returns an address. If the a formal access was successfully identified, and -/// access markers have not yet been removed, then the returned address is -/// produced by a begin_access marker. +/// Precondition: \p v must be an address. /// /// To get the base address of the formal access behind the access marker, /// either call stripAccessMarkers() on the returned value, or call /// getAccessedAddress() on \p v. /// -/// To identify the underlying storage object of the access, use -/// findAccessedStorage() on either \p v or the returned address. +/// To identify the underlying storage object of the access, call +/// findAccessedStorage() either on \p v or on the returned address. SILValue getAddressAccess(SILValue v); /// Convenience for stripAccessMarkers(getAddressAccess(v)). @@ -92,16 +136,18 @@ SILValue getAccessedAddress(SILValue v); /// let-variables are only written during let-variable initialization, which is /// assumed to store directly to the same, unaliased accessedAddress. /// -/// The address of a let-variable must be the base of a formal access . A 'let' -/// member of a struct is *not* a let-variable, because it's memory may be -/// written when formally modifying the outer struct. A let-variable is either -/// an entire local variable, global variable, or class property (this is the -/// definition of the base address of a formal access). +/// The address of a let-variable must be the base of a formal access, not an +/// access projection. A 'let' member of a struct is *not* a let-variable, +/// because it's memory may be written when formally modifying the outer +/// struct. A let-variable is either an entire local variable, global variable, +/// or class property (these are all formal access base addresses). /// /// The caller should derive the accessed address using /// stripAccessMarkers(getAccessedAddress(ptr)). bool isLetAddress(SILValue accessedAddress); +/// Return true if two accesses to the same storage may conflict given the kind +/// of each access. inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) { return !(a == SILAccessKind::Read && b == SILAccessKind::Read); } @@ -116,53 +162,56 @@ namespace swift { /// Represents the identity of a storage object being accessed. /// -/// AccessedStorage is carefully designed to solve three problems: +/// Requirements: /// -/// 1. Full specification and verification of SIL's model for exclusive -/// formal memory access, as enforced by "access markers". It is not a -/// model to encompass all SIL memory operations. +/// A bitwise comparable encoding and hash key to identify each location +/// being formally accessed. Any two accesses of "uniquely identified" +/// storage must have the same key if they access the same storage and +/// distinct keys if they access distinct storage. For more efficient +/// analysis, accesses to non-uniquely identified storage should have the +/// same key if they may point to the same storage. /// -/// 2. A bitwise comparable encoding and hash key to identify each location -/// being formally accessed. Any two accesses of uniquely identified storage -/// must have the same key if they access the same storage and distinct keys -/// if they access distinct storage. Accesses to non-uniquely identified -/// storage should ideally have the same key if they may point to the same -/// storage. +/// Complete identification of all class or global accesses. Failing to +/// identify a class or global access will introduce undefined program +/// behavior which can't be tested. /// -/// 3. Complete identification of all class or global accesses. Failing to -/// identify a class or global access will introduce undefined program -/// behavior which can't be tested. +/// Memory operations on "uniquely identified" storage cannot overlap with any +/// other memory operation on distinct "uniquely identified" storage. /// /// AccessedStorage may be one of several kinds of "identified" storage -/// objects, or may be valid, but Unidentified storage. An identified object -/// is known to identify the base of the accessed storage, whether that is a -/// SILValue that produces the base address, or a variable -/// declaration. "Uniquely identified" storage refers to identified storage that -/// cannot be aliased. For example, local allocations are uniquely identified, -/// while global variables and class properties are not. Unidentified storage is -/// associated with a SILValue that produces the accessed address but has not -/// been determined to be the base of a storage object. It may, for example, -/// be a SILPhiArgument. -/// -/// An invalid AccessedStorage object is marked Unidentified and contains an -/// invalid value. This signals that analysis has failed to recognize an -/// expected address producer pattern. Over time, more aggressive -/// SILVerification could allow the optimizer to aggressively assert that -/// AccessedStorage is always valid. +/// objects. Storage is "identified" when the base of the formal access is +/// recognized and the kind of storage precisely identified. The base is usually +/// represented by the SILValue that the memory address is derived from. For +/// global variable access, the base is the global's declaration instead. +/// +/// Unidentified *valid* storage is also associated with a SILValue that +/// produces the accessed address but that value has not been determined to be +/// the base of a formal access. It may be from a ref_tail_addr, undef, or some +/// recognized memory initialization pattern. Unidentified valid storage cannot +/// represent any arbitrary base address--it must at least been proven not to +/// correspond to any class or global variable access, unless it's nested within +/// another access to the same object. So, Unidentified can overlap with +/// Class/Global access, but it cannot be the only formal access to that memory. +/// +/// An *invalid* AccessedStorage object is Unidentified and associated with an +/// invalid SILValue. This signals that analysis has failed to recognize an +/// expected address producer pattern. +/// +/// An active goal is to enforce that every memory operation's +/// AccessedStorage is either valid or explicitly guarded by an "unsafe" +/// begin_access. /// /// Note that the SILValue that represents a storage object is not -/// necessarilly an address type. It may instead be a SILBoxType. -/// -/// AccessedStorage hashing and comparison (via DenseMapInfo) is used to -/// determine when two 'begin_access' instructions access the same or disjoint -/// underlying objects. -/// -/// `DenseMapInfo::isEqual()` guarantees that two AccessStorage values refer to -/// the same memory if both values are valid. -/// -/// `!DenseMapInfo::isEqual()` does not guarantee that two identified -/// AccessStorage values are distinct. Inequality does, however, guarantee that -/// two *uniquely* identified AccessStorage values are distinct. +/// necessarilly an address type. It may instead be a SILBoxType. So, even +/// though address phis are not allowed, finding the base of an access may +/// require traversing phis. +/// +/// Support for integer IDs and bitsets. An AccessedStorage value has enough +/// extra bits to store a unique index for each identified access in a +/// function. An AccessedStorage (without an ID) can be cheaply formed +/// on-the-fly for any memory operation then used as a hash key to lookup its +/// unique integer index which is stored directly in the hashed value but not +/// used as part of the hash key. class AccessedStorage { public: /// Enumerate over all valid begin_access bases. Clients can use a covered @@ -172,6 +221,7 @@ class AccessedStorage { Stack, Global, Class, + Tail, Argument, Yield, Nested, @@ -181,10 +231,16 @@ class AccessedStorage { static const char *getKindName(Kind k); - /// Directly create an AccessedStorage for class property access. + // Give object tail storage a fake property index for convenience. + static constexpr unsigned TailIndex = ~0U; + + /// Directly create an AccessedStorage for class or tail property access. static AccessedStorage forClass(SILValue object, unsigned propertyIndex) { AccessedStorage storage; - storage.initKind(Class, propertyIndex); + if (propertyIndex == TailIndex) + storage.initKind(Tail); + else + storage.initKind(Class, propertyIndex); storage.value = object; return storage; } @@ -254,7 +310,8 @@ class AccessedStorage { union { // For non-class storage, 'value' is the access base. For class storage // 'value' is the object base, where the access base is the class' stored - // property. + // property. For tail storage 'value' is the object base and there is no + // value for the access base. SILValue value; SILGlobalVariable *global; }; @@ -287,7 +344,7 @@ class AccessedStorage { } SILValue getValue() const { - assert(getKind() != Global && getKind() != Class); + assert(getKind() != Global && getKind() != Class && getKind() != Tail); return value; } @@ -296,9 +353,9 @@ class AccessedStorage { return getElementIndex(); } - SILArgument *getArgument() const { + SILFunctionArgument *getArgument() const { assert(getKind() == Argument); - return cast(value); + return cast(value); } SILGlobalVariable *getGlobal() const { @@ -307,7 +364,7 @@ class AccessedStorage { } SILValue getObject() const { - assert(getKind() == Class); + assert(getKind() == Class || getKind() == Tail); return value; } unsigned getPropertyIndex() const { @@ -327,6 +384,7 @@ class AccessedStorage { switch (getKind()) { case Box: case Stack: + case Tail: case Argument: case Yield: case Nested: @@ -349,6 +407,7 @@ class AccessedStorage { return true; case Global: case Class: + case Tail: case Argument: case Yield: case Nested: @@ -358,8 +417,19 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } + /// Return trye if the given access is guaranteed to be within a heap object. + bool isObjectAccess() const { + return getKind() == Class || getKind() == Tail; + } + + /// Return true if the given access is on a 'let' lvalue. bool isLetAccess(SILFunction *F) const; - + + /// If this is a uniquely identified formal access, then it cannot + /// alias with any other uniquely identified access to different storage. + /// + /// This determines whether access markers may conflict, so it cannot assume + /// that exclusivity is enforced. bool isUniquelyIdentified() const { switch (getKind()) { case Box: @@ -367,6 +437,7 @@ class AccessedStorage { case Global: return true; case Class: + case Tail: case Argument: case Yield: case Nested: @@ -376,27 +447,73 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } - bool isUniquelyIdentifiedOrClass() const { + /// Return true if this a uniquely identified formal access location assuming + /// exclusivity enforcement. Do not use this to optimize access markers. + bool isUniquelyIdentifiedAfterEnforcement() const { + if (isUniquelyIdentified()) + return true; + + return getKind() == Argument + && getArgument() + ->getArgumentConvention() + .isExclusiveIndirectParameter(); + } + + /// Return true if this identifies the base of a formal access location. + /// + /// Most formal access bases are uniquely identified, but class access + /// may alias other references to the same object. + bool isFormalAccessBase() const { if (isUniquelyIdentified()) return true; - return (getKind() == Class); + + return getKind() == Class; } + // Return true if this storage is guaranteed not to overlap with \p other's + // storage. bool isDistinctFrom(const AccessedStorage &other) const { - if (isUniquelyIdentified() && other.isUniquelyIdentified()) { - return !hasIdenticalBase(other); + if (isUniquelyIdentified()) { + if (other.isUniquelyIdentified() && !hasIdenticalBase(other)) + return true; + + if (other.isObjectAccess()) + return true; + + // We currently assume that Unidentified storage may overlap with + // Box/Stack storage. + return false; } - if (getKind() != Class || other.getKind() != Class) - // At least one side is an Argument or Yield, or is unidentified. + if (other.isUniquelyIdentified()) + return other.isDistinctFrom(*this); + + // Neither storage is uniquely identified. + if (isObjectAccess()) { + if (other.isObjectAccess()) { + // Property access cannot overlap with Tail access. + if (getKind() != other.getKind()) + return true; + + // We could also check if the object types are distinct, but that only + // helps if we know the relationships between class types. + return getKind() == Class + && getPropertyIndex() != other.getPropertyIndex(); + } + // Any type of nested/argument address may be within the same object. + // + // We also currently assume Unidentified access may be within an object + // purely to handle KeyPath accesses. The deriviation of the KeyPath + // address must separately appear to be a Class access so that all Class + // accesses are accounted for. return false; - - // Classes are not uniquely identified by their base. However, if the - // underling objects have identical types and distinct property indices then - // they are distinct storage locations. - if (getObject()->getType() == other.getObject()->getType() - && getPropertyIndex() != other.getPropertyIndex()) { - return true; } + if (other.isObjectAccess()) + return other.isDistinctFrom(*this); + + // Neither storage is from a class or tail. + // + // Unidentified values may alias with each other or with any kind of + // nested/argument access. return false; } @@ -423,6 +540,16 @@ class AccessedStorage { namespace llvm { /// Enable using AccessedStorage as a key in DenseMap. /// Do *not* include any extra pass data in key equality. +/// +/// AccessedStorage hashing and comparison is used to determine when two +/// 'begin_access' instructions access the same or disjoint underlying objects. +/// +/// `DenseMapInfo::isEqual()` guarantees that two AccessStorage values refer to +/// the same memory if both values are valid. +/// +/// `!DenseMapInfo::isEqual()` does not guarantee that two identified +/// AccessStorage values are distinct. Inequality does, however, guarantee that +/// two *uniquely* identified AccessStorage values are distinct. template <> struct DenseMapInfo { static swift::AccessedStorage getEmptyKey() { return swift::AccessedStorage(swift::SILValue::getFromOpaqueValue( @@ -448,10 +575,11 @@ template <> struct DenseMapInfo { return storage.getParamIndex(); case swift::AccessedStorage::Global: return DenseMapInfo::getHashValue(storage.getGlobal()); - case swift::AccessedStorage::Class: { + case swift::AccessedStorage::Class: return llvm::hash_combine(storage.getObject(), storage.getPropertyIndex()); - } + case swift::AccessedStorage::Tail: + return DenseMapInfo::getHashValue(storage.getObject()); } llvm_unreachable("Unhandled AccessedStorageKind"); } @@ -464,37 +592,50 @@ template <> struct DenseMapInfo { namespace swift { -/// Given an address accessed by an instruction that reads or modifies -/// memory, return an AccessedStorage object that identifies the formal access. -/// -/// The returned AccessedStorage represents the best attempt to find the base of -/// the storage object being accessed at `sourceAddr`. This may be a fully -/// identified storage base of known kind, or a valid but Unidentified storage -/// object, such as a SILPhiArgument. -/// -/// This may return an invalid storage object if the address producer is not -/// recognized by a whitelist of recognizable access patterns. The result must -/// always be valid when `sourceAddr` is used for formal memory access, i.e. as -/// the operand of begin_access. +/// Given an address used by an instruction that reads or writes memory, return +/// the AccessedStorage value that identifies the formally accessed memory, +/// looking through any nested formal accesses to find the underlying storage. /// -/// If `sourceAddr` is produced by a begin_access, this returns a Nested -/// AccessedStorage kind. This is useful for exclusivity checking to distinguish -/// between a nested access vs. a conflict. +/// This may return invalid storage for a memory operation that is not part of +/// a formal access or when the outermost formal access has Unsafe enforcement. AccessedStorage findAccessedStorage(SILValue sourceAddr); -/// Given an address accessed by an instruction that reads or modifies -/// memory, return an AccessedStorage that identifies the formal access, looking -/// through any Nested access to find the original storage. +// Helper for identifyFormalAccess. +AccessedStorage identifyAccessedStorageImpl(SILValue sourceAddr); + +/// Return an AccessedStorage object that identifies the formal access +/// represented by \p beginAccess. /// -/// This is identical to findAccessedStorage(), but never returns Nested -/// storage and may return invalid storage for nested access when the outer -/// access has Unsafe enforcement. -AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr); +/// If the given access is nested within an outer access, return a Nested +/// AccessedStorage kind. This is useful for exclusivity checking to distinguish +/// between nested access vs. conflicting access on the same storage. +/// +/// May return an invalid storage for either: +/// - A \p beginAccess with Unsafe enforcement +/// - Non-OSSA form in which address-type block args are allowed +inline AccessedStorage identifyFormalAccess(BeginAccessInst *beginAccess) { + return identifyAccessedStorageImpl(beginAccess->getSource()); +} + +inline AccessedStorage +identifyFormalAccess(BeginUnpairedAccessInst *beginAccess) { + return identifyAccessedStorageImpl(beginAccess->getSource()); +} + +/// Return a valid AccessedStorage object for an address captured by a no-escape +/// closure. A no-escape closure may capture a regular storage address without +/// guarding it with an access marker. If the captured address does come from an +/// access marker, then this returns a Nested AccessedStorage kind. +inline AccessedStorage identifyCapturedStorage(SILValue capturedAddress) { + auto storage = identifyAccessedStorageImpl(capturedAddress); + assert(storage && "captured access has invalid storage"); + return storage; +} } // end namespace swift //===----------------------------------------------------------------------===// -// MARK: Helper API +// MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// namespace swift { @@ -536,11 +677,11 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg); /// Return true if the given address producer may be the source of a formal /// access (a read or write of a potentially aliased, user visible variable). /// -/// `storage` must be a valid AccessedStorage object. +/// `storage` must be a valid, non-nested AccessedStorage object. /// /// If this returns false, then the address can be safely accessed without /// a begin_access marker. To determine whether to emit begin_access: -/// storage = findAccessedStorage(address) +/// storage = identifyFormalAccess(address) /// needsAccessMarker = storage && isPossibleFormalAccessBase(storage) /// /// Warning: This is only valid for SIL with well-formed accesses. For example, @@ -575,22 +716,19 @@ class AccessUseDefChainVisitor { Impl &asImpl() { return static_cast(*this); } public: - // Subclasses can provide a method for any identified access base: - // Result visitBase(SILValue base, AccessedStorage::Kind kind); - - // Visitors for specific identified access kinds. These default to calling out - // to visitIdentified. - Result visitClassAccess(RefElementAddrInst *field) { return asImpl().visitBase(field, AccessedStorage::Class); } + Result visitTailAccess(RefTailAddrInst *tail) { + return asImpl().visitBase(tail, AccessedStorage::Tail); + } Result visitArgumentAccess(SILFunctionArgument *arg) { return asImpl().visitBase(arg, AccessedStorage::Argument); } Result visitBoxAccess(AllocBoxInst *box) { return asImpl().visitBase(box, AccessedStorage::Box); } - /// The argument may be either a GlobalAddrInst or the ApplyInst for a global + /// \p global may be either a GlobalAddrInst or the ApplyInst for a global /// accessor function. Result visitGlobalAccess(SILValue global) { return asImpl().visitBase(global, AccessedStorage::Global); @@ -604,192 +742,190 @@ class AccessUseDefChainVisitor { Result visitNestedAccess(BeginAccessInst *access) { return asImpl().visitBase(access, AccessedStorage::Nested); } - - // Visitors for unidentified base values. - Result visitUnidentified(SILValue base) { return asImpl().visitBase(base, AccessedStorage::Unidentified); } - // Subclasses must provide implementations to visit non-access bases - // and phi arguments, and for incomplete projections from the access: - // void visitNonAccess(SILValue base); - // void visitPhi(SILPhiArgument *phi); - // void visitIncomplete(SILValue projectedAddr, SILValue parentAddr); + // Subclasses must provide implementations for: + // + // Result visitBase(SILValue base, AccessedStorage::Kind kind); + // Result visitNonAccess(SILValue base); + // Result visitPhi(SILPhiArgument *phi); + // Result visitCast(SingleValueInstruction *cast, Operand *parentAddr); + // Result visitPathComponent(SingleValueInstruction *projectedAddr, + // Operand *parentAddr); Result visit(SILValue sourceAddr); }; template Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { - // Handle immediately-identifiable instructions. - while (true) { - switch (sourceAddr->getKind()) { - // An AllocBox is a fully identified memory location. - case ValueKind::AllocBoxInst: - return asImpl().visitBoxAccess(cast(sourceAddr)); - // An AllocStack is a fully identified memory location, which may occur - // after inlining code already subjected to stack promotion. - case ValueKind::AllocStackInst: - return asImpl().visitStackAccess(cast(sourceAddr)); - case ValueKind::GlobalAddrInst: - return asImpl().visitGlobalAccess(sourceAddr); - case ValueKind::ApplyInst: { - FullApplySite apply(cast(sourceAddr)); - if (auto *funcRef = apply.getReferencedFunctionOrNull()) { - if (getVariableOfGlobalInit(funcRef)) { - return asImpl().visitGlobalAccess(sourceAddr); - } + switch (sourceAddr->getKind()) { + default: + if (isAddressForLocalInitOnly(sourceAddr)) + return asImpl().visitUnidentified(sourceAddr); + return asImpl().visitNonAccess(sourceAddr); + + // MARK: Handle immediately-identifiable instructions. + + // An AllocBox is a fully identified memory location. + case ValueKind::AllocBoxInst: + return asImpl().visitBoxAccess(cast(sourceAddr)); + // An AllocStack is a fully identified memory location, which may occur + // after inlining code already subjected to stack promotion. + case ValueKind::AllocStackInst: + return asImpl().visitStackAccess(cast(sourceAddr)); + case ValueKind::GlobalAddrInst: + return asImpl().visitGlobalAccess(sourceAddr); + case ValueKind::ApplyInst: { + FullApplySite apply(cast(sourceAddr)); + if (auto *funcRef = apply.getReferencedFunctionOrNull()) { + if (getVariableOfGlobalInit(funcRef)) { + return asImpl().visitGlobalAccess(sourceAddr); } - // Try to classify further below. - break; - } - case ValueKind::RefElementAddrInst: - return asImpl().visitClassAccess(cast(sourceAddr)); - // A yield is effectively a nested access, enforced independently in - // the caller and callee. - case ValueKind::BeginApplyResult: - return asImpl().visitYieldAccess(cast(sourceAddr)); - // A function argument is effectively a nested access, enforced - // independently in the caller and callee. - case ValueKind::SILFunctionArgument: - return asImpl().visitArgumentAccess(cast(sourceAddr)); - // View the outer begin_access as a separate location because nested - // accesses do not conflict with each other. - case ValueKind::BeginAccessInst: - return asImpl().visitNestedAccess(cast(sourceAddr)); - default: - // Try to classify further below. - break; } - - // If the sourceAddr producer cannot immediately be classified, follow the - // use-def chain of sourceAddr, box, or RawPointer producers. - assert(sourceAddr->getType().isAddress() - || isa(sourceAddr->getType().getASTType()) - || isa(sourceAddr->getType().getASTType())); - - // Handle other unidentified address sources. - switch (sourceAddr->getKind()) { - default: - if (isAddressForLocalInitOnly(sourceAddr)) - return asImpl().visitUnidentified(sourceAddr); - return asImpl().visitNonAccess(sourceAddr); - - case ValueKind::SILUndef: + if (isExternalGlobalAddressor(cast(sourceAddr))) return asImpl().visitUnidentified(sourceAddr); - case ValueKind::ApplyInst: - if (isExternalGlobalAddressor(cast(sourceAddr))) - return asImpl().visitUnidentified(sourceAddr); - - // Don't currently allow any other calls to return an accessed address. - return asImpl().visitNonAccess(sourceAddr); - - case ValueKind::StructExtractInst: - // Handle nested access to a KeyPath projection. The projection itself - // uses a Builtin. However, the returned UnsafeMutablePointer may be - // converted to an address and accessed via an inout argument. - if (isUnsafePointerExtraction(cast(sourceAddr))) - return asImpl().visitUnidentified(sourceAddr); - return asImpl().visitNonAccess(sourceAddr); - - case ValueKind::SILPhiArgument: { - auto *phiArg = cast(sourceAddr); - if (phiArg->isPhiArgument()) { - return asImpl().visitPhi(phiArg); - } - - // A non-phi block argument may be a box value projected out of - // switch_enum. Address-type block arguments are not allowed. - if (sourceAddr->getType().isAddress()) - return asImpl().visitNonAccess(sourceAddr); - - checkSwitchEnumBlockArg(cast(sourceAddr)); + // Don't currently allow any other calls to return an accessed address. + return asImpl().visitNonAccess(sourceAddr); + } + case ValueKind::RefElementAddrInst: + return asImpl().visitClassAccess(cast(sourceAddr)); + // A yield is effectively a nested access, enforced independently in + // the caller and callee. + case ValueKind::BeginApplyResult: + return asImpl().visitYieldAccess(cast(sourceAddr)); + // A function argument is effectively a nested access, enforced + // independently in the caller and callee. + case ValueKind::SILFunctionArgument: + return asImpl().visitArgumentAccess(cast(sourceAddr)); + + // View the outer begin_access as a separate location because nested + // accesses do not conflict with each other. + case ValueKind::BeginAccessInst: + return asImpl().visitNestedAccess(cast(sourceAddr)); + + case ValueKind::SILUndef: + return asImpl().visitUnidentified(sourceAddr); + + // MARK: The sourceAddr producer cannot immediately be classified, + // follow the use-def chain. + + case ValueKind::StructExtractInst: + // Handle nested access to a KeyPath projection. The projection itself + // uses a Builtin. However, the returned UnsafeMutablePointer may be + // converted to an address and accessed via an inout argument. + if (isUnsafePointerExtraction(cast(sourceAddr))) return asImpl().visitUnidentified(sourceAddr); + return asImpl().visitNonAccess(sourceAddr); + + case ValueKind::SILPhiArgument: { + auto *phiArg = cast(sourceAddr); + if (phiArg->isPhiArgument()) { + return asImpl().visitPhi(phiArg); } - // Load a box from an indirect payload of an opaque enum. - // We must have peeked past the project_box earlier in this loop. - // (the indirectness makes it a box, the load is for address-only). - // - // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case - // %box = load [take] %payload_adr : $*{ var Enum } - // - // FIXME: this case should go away with opaque values. - // - // Otherwise return invalid AccessedStorage. - case ValueKind::LoadInst: - if (sourceAddr->getType().is()) { - SILValue operAddr = cast(sourceAddr)->getOperand(); - assert(isa(operAddr)); - return asImpl().visitIncomplete(sourceAddr, operAddr); - } - return asImpl().visitNonAccess(sourceAddr); - // ref_tail_addr project an address from a reference. - // This is a valid address producer for nested @inout argument - // access, but it is never used for formal access of identified objects. - case ValueKind::RefTailAddrInst: - return asImpl().visitUnidentified(sourceAddr); + // A non-phi block argument may be a box value projected out of + // switch_enum. Address-type block arguments are not allowed. + if (sourceAddr->getType().isAddress()) + return asImpl().visitNonAccess(sourceAddr); - // Inductive single-operand cases: - // Look through address casts to find the source address. - case ValueKind::MarkUninitializedInst: - case ValueKind::OpenExistentialAddrInst: - case ValueKind::UncheckedAddrCastInst: - // Inductive cases that apply to any type. - case ValueKind::CopyValueInst: - case ValueKind::MarkDependenceInst: - // Look through a project_box to identify the underlying alloc_box as the - // accesed object. It must be possible to reach either the alloc_box or the - // containing enum in this loop, only looking through simple value - // propagation such as copy_value. - case ValueKind::ProjectBoxInst: - // Handle project_block_storage just like project_box. - case ValueKind::ProjectBlockStorageInst: - // Look through begin_borrow in case a local box is borrowed. - case ValueKind::BeginBorrowInst: - return asImpl().visitIncomplete(sourceAddr, - cast(sourceAddr)->getOperand(0)); - - // Access to a Builtin.RawPointer. Treat this like the inductive cases above - // because some RawPointers originate from identified locations. See the - // special case for global addressors, which return RawPointer, - // above. AddressToPointer is also handled because it results from inlining - // a global addressor without folding the - // AddressToPointer->PointerToAddress. - // - // If the inductive search does not find a valid addressor, it will - // eventually reach the default case that returns in invalid location. This - // is correct for RawPointer because, although accessing a RawPointer is - // legal SIL, there is no way to guarantee that it doesn't access class or - // global storage, so returning a valid unidentified storage object would be - // incorrect. It is the caller's responsibility to know that formal access - // to such a location can be safely ignored. - // - // For example: - // - // - KeyPath Builtins access RawPointer. However, the caller can check - // that the access `isFromBuilin` and ignore the storage. - // - // - lldb generates RawPointer access for debugger variables, but SILGen - // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the - // AccessedStorage for 'Unsafe' access. - case ValueKind::PointerToAddressInst: - case ValueKind::AddressToPointerInst: - return asImpl().visitIncomplete(sourceAddr, - cast(sourceAddr)->getOperand(0)); - - // Address-to-address subobject projections. - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::TailAddrInst: - case ValueKind::IndexAddrInst: - return asImpl().visitIncomplete(sourceAddr, - cast(sourceAddr)->getOperand(0)); + checkSwitchEnumBlockArg(cast(sourceAddr)); + return asImpl().visitUnidentified(sourceAddr); + } + // Load a box from an indirect payload of an opaque enum. + // We must have peeked past the project_box earlier in this loop. + // (the indirectness makes it a box, the load is for address-only). + // + // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case + // %box = load [take] %payload_adr : $*{ var Enum } + // + // FIXME: this case should go away with opaque values. + // + // Otherwise return invalid AccessedStorage. + case ValueKind::LoadInst: + if (sourceAddr->getType().is()) { + Operand *addrOper = &cast(sourceAddr)->getOperandRef(); + assert(isa(addrOper->get())); + return asImpl().visitCast(cast(sourceAddr), + addrOper); } - }; + return asImpl().visitNonAccess(sourceAddr); + + // ref_tail_addr project an address from a reference. + // This is a valid address producer for nested @inout argument + // access, but it is never used for formal access of identified objects. + case ValueKind::RefTailAddrInst: + return asImpl().visitTailAccess(cast(sourceAddr)); + + // Inductive single-operand cases: + // Look through address casts to find the source address. + case ValueKind::MarkUninitializedInst: + case ValueKind::OpenExistentialAddrInst: + case ValueKind::UncheckedAddrCastInst: + // Inductive cases that apply to any type. + case ValueKind::CopyValueInst: + case ValueKind::MarkDependenceInst: + // Look through a project_box to identify the underlying alloc_box as the + // accesed object. It must be possible to reach either the alloc_box or the + // containing enum in this loop, only looking through simple value + // propagation such as copy_value. + case ValueKind::ProjectBoxInst: + // Handle project_block_storage just like project_box. + case ValueKind::ProjectBlockStorageInst: + // Look through begin_borrow in case a local box is borrowed. + case ValueKind::BeginBorrowInst: + // Casting to RawPointer does not affect the AccessPath. When converting + // between address types, they must be layout compatible (with truncation). + case ValueKind::AddressToPointerInst: + // A tail_addr is a projection that does not affect the access path because it + // must always originate from a ref_tail_addr. Any projection within the + // object's tail storage effectively has the same access path. + case ValueKind::TailAddrInst: + return asImpl().visitCast( + cast(sourceAddr), + &cast(sourceAddr)->getAllOperands()[0]); + + // Access to a Builtin.RawPointer. It may be important to continue looking + // through this because some RawPointers originate from identified + // locations. See the special case for global addressors, which return + // RawPointer, above. + // + // If the inductive search does not find a valid addressor, it will + // eventually reach the default case that returns in invalid location. This + // is correct for RawPointer because, although accessing a RawPointer is + // legal SIL, there is no way to guarantee that it doesn't access class or + // global storage, so returning a valid unidentified storage object would be + // incorrect. It is the caller's responsibility to know that formal access + // to such a location can be safely ignored. + // + // For example: + // + // - KeyPath Builtins access RawPointer. However, the caller can check + // that the access `isFromBuilin` and ignore the storage. + // + // - lldb generates RawPointer access for debugger variables, but SILGen + // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the + // AccessedStorage for 'Unsafe' access. + // + // This is always considered a path component because an IndexAddr may + // project from it. + case ValueKind::PointerToAddressInst: + return asImpl().visitPathComponent( + cast(sourceAddr), + &cast(sourceAddr)->getAllOperands()[0]); + + // Address-to-address subobject projections. Projection::isAddressProjection + // returns true for these. + case ValueKind::StructElementAddrInst: + case ValueKind::TupleElementAddrInst: + case ValueKind::UncheckedTakeEnumDataAddrInst: + case ValueKind::IndexAddrInst: + return asImpl().visitPathComponent( + cast(sourceAddr), + &cast(sourceAddr)->getAllOperands()[0]); + } } } // end namespace swift diff --git a/include/swift/SIL/OptimizationRemark.h b/include/swift/SIL/OptimizationRemark.h index 20d59e0cf55ab..47318c4cc6765 100644 --- a/include/swift/SIL/OptimizationRemark.h +++ b/include/swift/SIL/OptimizationRemark.h @@ -19,8 +19,10 @@ #ifndef SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H #define SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H +#include "swift/AST/SemanticAttrs.h" #include "swift/Basic/SourceLoc.h" #include "swift/Demangling/Demangler.h" +#include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" @@ -32,17 +34,75 @@ class SILFunction; namespace OptRemark { +struct ArgumentKeyKind { + enum InnerTy { + // Just assume this is a normal msg that we are emitting. + Default, + + // Assume this is a note that should be emitted as a separate + // diagnostic when emitting diagnostics. Do nothing special + // along the backend path. + Note, + + // Assume that this is a note that should be emitted as a separate + // diagnostic but that doesn't have its own source loc: we should reuse the + // one for the original remark. + // + // This is intended to be used in situations where one needs to emit a + // "note" warning due to us not being able to infer a part of our + // opt-remark. + ParentLocNote, + }; + + InnerTy innerValue; + + ArgumentKeyKind(InnerTy value) : innerValue(value) {} + ArgumentKeyKind(const ArgumentKeyKind &kind) : innerValue(kind.innerValue) {} + + operator InnerTy() const { return innerValue; } + + /// Return true if this argument is meant to be a separate diagnostic when we + /// emit diagnostics but when we emit to the remark streamer (due to not + /// having support for this), we just emit the remark inline. + /// + /// TODO: Unfortunate that this needs to be done. + bool isSeparateDiagnostic() const { + switch (innerValue) { + case InnerTy::Default: + return false; + case InnerTy::Note: + case InnerTy::ParentLocNote: + return true; + } + + llvm_unreachable("Covered switch isn't covered?!"); + } +}; + +struct ArgumentKey { + ArgumentKeyKind kind; + std::string data; + + ArgumentKey(ArgumentKeyKind kind, StringRef data) : kind(kind), data(data) {} + ArgumentKey(ArgumentKeyKind::InnerTy kind, StringRef data) + : kind(kind), data(data) {} + ArgumentKey(ArgumentKey kind, StringRef data) : kind(kind.kind), data(data) {} +}; + /// Used in the streaming interface as the general argument type. It /// internally converts everything into a key-value pair. struct Argument { - std::string key; + ArgumentKey key; std::string val; /// If set, the debug location corresponding to the value. SourceLoc loc; - explicit Argument(StringRef Str = "") : key("String"), val(Str) {} - Argument(StringRef key, StringRef val) : key(key), val(val) {} + explicit Argument(StringRef str = "") + : Argument({ArgumentKeyKind::Default, "String"}, str) {} + Argument(StringRef key, StringRef val) + : Argument({ArgumentKeyKind::Default, key}, val) {} + Argument(ArgumentKey key, StringRef val) : key(key), val(val) {} Argument(StringRef key, int n); Argument(StringRef key, long n); Argument(StringRef key, long long n); @@ -50,9 +110,16 @@ struct Argument { Argument(StringRef key, unsigned long n); Argument(StringRef key, unsigned long long n); - Argument(StringRef key, SILFunction *f); + Argument(StringRef key, SILFunction *f) + : Argument(ArgumentKey(ArgumentKeyKind::Default, key), f) {} + Argument(ArgumentKey key, SILFunction *f); Argument(StringRef key, SILType ty); Argument(StringRef key, CanType ty); + + Argument(StringRef key, StringRef msg, const ValueDecl *decl) + : Argument(ArgumentKey(ArgumentKeyKind::Default, key), msg, decl) {} + Argument(ArgumentKey key, StringRef msg, const ValueDecl *decl) + : key(key), val(msg), loc(decl->getLoc()) {} }; /// Shorthand to insert named-value pairs. @@ -65,10 +132,29 @@ struct IndentDebug { unsigned width; }; +enum class SourceLocInferenceBehavior { + None, + ForwardScanOnly, + BackwardScanOnly, + ForwardThenBackward, + BackwardThenForward, +}; + +/// Infer the proper SourceLoc to use for the given SILInstruction. +/// +/// This means that if we have a valid location for the instruction, we just +/// return that. Otherwise, we have a runtime instruction that does not have a +/// valid source location. In such a case, we infer the source location from the +/// surrounding code. If we can not find any surrounding code, we return an +/// invalid SourceLoc. +SourceLoc inferOptRemarkSourceLoc(SILInstruction &i, + SourceLocInferenceBehavior inferBehavior); + /// The base class for remarks. This can be created by optimization passed to /// report successful and unsuccessful optimizations. CRTP is used to preserve /// the underlying type encoding the remark kind in the insertion operator. -template class Remark { +template +class Remark { /// Arguments collected via the streaming interface. SmallVector args; @@ -93,9 +179,10 @@ template class Remark { unsigned indentDebugWidth = 0; protected: - Remark(StringRef identifier, SILInstruction &i) + Remark(StringRef identifier, SILInstruction &i, + SourceLocInferenceBehavior inferenceBehavior) : identifier((Twine("sil.") + identifier).str()), - location(i.getLoc().getSourceLoc()), + location(inferOptRemarkSourceLoc(i, inferenceBehavior)), function(i.getParent()->getParent()), demangledFunctionName(Demangle::demangleSymbolAsString( function->getName(), @@ -133,17 +220,25 @@ template class Remark { /// Remark to report a successful optimization. struct RemarkPassed : public Remark { - RemarkPassed(StringRef id, SILInstruction &i) : Remark(id, i) {} + RemarkPassed(StringRef id, SILInstruction &i) + : Remark(id, i, SourceLocInferenceBehavior::None) {} + RemarkPassed(StringRef id, SILInstruction &i, + SourceLocInferenceBehavior inferenceBehavior) + : Remark(id, i, inferenceBehavior) {} }; /// Remark to report a unsuccessful optimization. struct RemarkMissed : public Remark { - RemarkMissed(StringRef id, SILInstruction &i) : Remark(id, i) {} + RemarkMissed(StringRef id, SILInstruction &i) + : Remark(id, i, SourceLocInferenceBehavior::None) {} + RemarkMissed(StringRef id, SILInstruction &i, + SourceLocInferenceBehavior inferenceBehavior) + : Remark(id, i, inferenceBehavior) {} }; /// Used to emit the remarks. Passes reporting remarks should create an /// instance of this. class Emitter { - SILModule &module; + SILFunction &fn; std::string passName; bool passedEnabled; bool missedEnabled; @@ -157,7 +252,7 @@ class Emitter { template bool isEnabled(); public: - Emitter(StringRef passName, SILModule &m); + Emitter(StringRef passName, SILFunction &fn); /// Take a lambda that returns a remark which will be emitted. The /// lambda is not evaluated unless remarks are enabled. Second argument is @@ -166,7 +261,7 @@ class Emitter { void emit(T remarkBuilder, decltype(remarkBuilder()) * = nullptr) { using RemarkT = decltype(remarkBuilder()); // Avoid building the remark unless remarks are enabled. - if (isEnabled() || module.getSILRemarkStreamer()) { + if (isEnabled() || fn.getModule().getSILRemarkStreamer()) { auto rb = remarkBuilder(); rb.setPassName(passName); emit(rb); @@ -180,8 +275,9 @@ class Emitter { decltype(remarkBuilder()) * = nullptr) { using RemarkT = decltype(remarkBuilder()); // Avoid building the remark unless remarks are enabled. - bool emitRemark = emitter && (emitter->isEnabled() || - emitter->module.getSILRemarkStreamer()); + bool emitRemark = + emitter && (emitter->isEnabled() || + emitter->fn.getModule().getSILRemarkStreamer()); // Same for DEBUG. bool shouldEmitDebug = false; #ifndef NDEBUG diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index d02089ae9380b..0202e099d1caa 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -247,7 +247,8 @@ class Projection { Projection &operator=(Projection &&P) = default; - bool isValid() const { return Value.isValid(); } + bool isValid() const { return bool(*this); } + operator bool() const { return Value.isValid(); } /// Convenience method for getting the underlying index. Assumes that this /// projection is valid. Otherwise it asserts. diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 8eafabda8a62d..f4d4bc1c4733f 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -725,6 +725,27 @@ class SILBuilder { return lowering.emitLoad(*this, Loc, LV, Qualifier); } + /// Convenience function for calling emitLoad on the type lowering for + /// non-address values. + SILValue emitLoweredLoadValueOperation( + SILLocation Loc, SILValue LV, LoadOwnershipQualifier Qualifier, + Lowering::TypeLowering::TypeExpansionKind ExpansionKind) { + assert(isLoadableOrOpaque(LV->getType())); + const auto &lowering = getTypeLowering(LV->getType()); + return lowering.emitLoweredLoad(*this, Loc, LV, Qualifier, ExpansionKind); + } + + /// Convenience function for calling emitLoweredStore on the type lowering for + /// non-address values. + void emitLoweredStoreValueOperation( + SILLocation Loc, SILValue Value, SILValue Addr, + StoreOwnershipQualifier Qual, + Lowering::TypeLowering::TypeExpansionKind ExpansionKind) { + assert(isLoadableOrOpaque(Value->getType())); + const auto &lowering = getTypeLowering(Value->getType()); + lowering.emitLoweredStore(*this, Loc, Value, Addr, Qual, ExpansionKind); + } + LoadBorrowInst *createLoadBorrow(SILLocation Loc, SILValue LV) { assert(isLoadableOrOpaque(LV->getType()) && !LV->getType().isTrivial(getFunction())); @@ -2186,6 +2207,16 @@ class SILBuilder { return lowering.emitCopyValue(*this, Loc, v); } + /// Convenience function for calling emitCopy on the type lowering + /// for the non-address value. + SILValue emitLoweredCopyValueOperation( + SILLocation Loc, SILValue v, + Lowering::TypeLowering::TypeExpansionKind expansionKind) { + assert(!v->getType().isAddress()); + auto &lowering = getTypeLowering(v->getType()); + return lowering.emitLoweredCopyValue(*this, Loc, v, expansionKind); + } + /// Convenience function for calling TypeLowering.emitDestroy on the type /// lowering for the non-address value. void emitDestroyValueOperation(SILLocation Loc, SILValue v) { @@ -2196,6 +2227,18 @@ class SILBuilder { lowering.emitDestroyValue(*this, Loc, v); } + /// Convenience function for calling TypeLowering.emitDestroy on the type + /// lowering for the non-address value. + void emitLoweredDestroyValueOperation( + SILLocation Loc, SILValue v, + Lowering::TypeLowering::TypeExpansionKind expansionKind) { + assert(!v->getType().isAddress()); + if (F->hasOwnership() && v.getOwnershipKind() == ValueOwnershipKind::None) + return; + auto &lowering = getTypeLowering(v->getType()); + lowering.emitLoweredDestroyValue(*this, Loc, v, expansionKind); + } + /// Convenience function for destroying objects and addresses. /// /// Objects are destroyed using emitDestroyValueOperation and addresses by diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index ae3f940d21ad4..8125736fc391f 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -274,6 +274,9 @@ class SILFunction /// that it may have unboxed capture (i.e. @inout_aliasable parameters). unsigned IsWithoutActuallyEscapingThunk : 1; + /// True if this function is an async function. + unsigned IsAsync : 1; + /// If != OptimizationMode::NotSet, the optimization mode specified with an /// function attribute. unsigned OptMode : NumOptimizationModeBits; @@ -501,6 +504,10 @@ class SILFunction IsWithoutActuallyEscapingThunk = val; } + bool isAsync() const { return IsAsync; } + + void setAsync(bool val = true) { IsAsync = val; } + /// Returns the calling convention used by this entry point. SILFunctionTypeRepresentation getRepresentation() const { return getLoweredFunctionType()->getRepresentation(); diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index da0b8d36ebd19..d7620461af539 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -260,8 +260,7 @@ SILFunction *getCalleeOfOnceCall(BuiltinInst *BI); /// Given an addressor, AddrF, find the call to the global initializer if /// present, otherwise return null. If an initializer is returned, then /// `CallToOnce` is initialized to the corresponding builtin "once" call. -SILFunction *findInitializer(SILModule *Module, SILFunction *AddrF, - BuiltinInst *&CallToOnce); +SILFunction *findInitializer(SILFunction *AddrF, BuiltinInst *&CallToOnce); /// Helper for getVariableOfGlobalInit(), so GlobalOpts can deeply inspect and /// rewrite the initialization pattern. diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index dd9e3731b9ac9..d504f37085831 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3307,7 +3307,6 @@ class StringLiteralInst final enum class Encoding { Bytes, UTF8, - UTF16, /// UTF-8 encoding of an Objective-C selector. ObjCSelector, }; diff --git a/include/swift/SIL/SILLocation.h b/include/swift/SIL/SILLocation.h index c57af52aa3ff5..d847221805743 100644 --- a/include/swift/SIL/SILLocation.h +++ b/include/swift/SIL/SILLocation.h @@ -538,7 +538,8 @@ class RegularLocation : public SILLocation { /// it may have been generated from. These locations will have an artificial /// line location of zero in DWARF, but in CodeView we want to use the given /// line since line zero does not represent an artificial line in CodeView. - static RegularLocation getAutoGeneratedLocation(SourceLoc L) { + template + static RegularLocation getAutoGeneratedLocation(InputLocTy L) { RegularLocation AL(L); AL.markAutoGenerated(); return AL; @@ -755,17 +756,18 @@ class SILDebugScope; /// A SILLocation paired with a SILDebugScope. class SILDebugLocation { - const SILDebugScope *Scope = nullptr; - SILLocation Location; + const SILDebugScope *debugScope; + SILLocation location; public: SILDebugLocation() - : Scope(nullptr), - Location(RegularLocation::getAutoGeneratedLocation()) {} - SILDebugLocation(SILLocation Loc, const SILDebugScope *DS) - : Scope(DS), Location(Loc) {} - SILLocation getLocation() const { return Location; } - const SILDebugScope *getScope() const { return Scope; } + : debugScope(nullptr), + location(RegularLocation::getAutoGeneratedLocation()) {} + SILDebugLocation(SILLocation location, const SILDebugScope *debugScope) + : debugScope(debugScope), location(location) {} + SILLocation getLocation() const { return location; } + const SILDebugScope *getScope() const { return debugScope; } + operator bool() const { return bool(location) && debugScope; } }; } // end swift namespace diff --git a/include/swift/SIL/SILRemarkStreamer.h b/include/swift/SIL/SILRemarkStreamer.h index 2ee8f45e120f2..8e9106f0cdef6 100644 --- a/include/swift/SIL/SILRemarkStreamer.h +++ b/include/swift/SIL/SILRemarkStreamer.h @@ -111,7 +111,7 @@ llvm::remarks::Remark SILRemarkStreamer::toLLVMRemark( for (const OptRemark::Argument &arg : optRemark.getArgs()) { llvmRemark.Args.emplace_back(); - llvmRemark.Args.back().Key = arg.key; + llvmRemark.Args.back().Key = arg.key.data; llvmRemark.Args.back().Val = arg.val; llvmRemark.Args.back().Loc = toRemarkLocation(arg.loc, getASTContext().SourceMgr); diff --git a/include/swift/SIL/SILVTable.h b/include/swift/SIL/SILVTable.h index cd9f9c0a87c4c..8a5c5866f6f04 100644 --- a/include/swift/SIL/SILVTable.h +++ b/include/swift/SIL/SILVTable.h @@ -64,7 +64,8 @@ class SILVTableEntry { // Please update the PointerIntPair above if you add/remove enums. }; - SILVTableEntry() : ImplAndKind(nullptr, Kind::Normal) {} + SILVTableEntry() : ImplAndKind(nullptr, Kind::Normal), + IsNonOverridden(false) {} SILVTableEntry(SILDeclRef Method, SILFunction *Implementation, Kind TheKind, bool NonOverridden) @@ -80,6 +81,19 @@ class SILVTableEntry { void setNonOverridden(bool value) { IsNonOverridden = value; } SILFunction *getImplementation() const { return ImplAndKind.getPointer(); } + + void print(llvm::raw_ostream &os) const; + + bool operator==(const SILVTableEntry &e) const { + return Method == e.Method + && getImplementation() == e.getImplementation() + && getKind() == e.getKind() + && isNonOverridden() == e.isNonOverridden(); + } + + bool operator!=(const SILVTableEntry &e) const { + return !(*this == e); + } }; /// A mapping from each dynamically-dispatchable method of a class to the @@ -140,9 +154,13 @@ class SILVTable final : public SILAllocated, } /// Return all of the method entries mutably. + /// If you do modify entries, make sure to invoke `updateVTableCache` to update the + /// SILModule's cache entry. MutableArrayRef getMutableEntries() { return {getTrailingObjects(), NumEntries}; } + + void updateVTableCache(const Entry &entry); /// Look up the implementation function for the given method. Optional getEntry(SILModule &M, SILDeclRef method) const; diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 42db005ea3bdf..aba919ee0090c 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -57,7 +57,8 @@ CanAnyFunctionType adjustFunctionType(CanAnyFunctionType type, /// Change the given function type's representation. inline CanAnyFunctionType adjustFunctionType(CanAnyFunctionType t, SILFunctionType::Representation rep) { - auto extInfo = t->getExtInfo().withSILRepresentation(rep); + auto extInfo = + t->getExtInfo().intoBuilder().withSILRepresentation(rep).build(); return adjustFunctionType(t, extInfo); } @@ -390,6 +391,28 @@ class TypeLowering { ///> types. }; + /// Emit a load from \p addr given the LoadOwnershipQualifier \p qual. + /// + /// This abstracts over the differences in between trivial and non-trivial + /// types and lets one specify an expansion kind that gets passed to any + /// copy_value that we create. + virtual SILValue emitLoweredLoad( + SILBuilder &B, SILLocation loc, SILValue addr, + LoadOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind expansionKind) const = 0; + + /// Emit a store of \p value into \p addr given the StoreOwnershipQualifier + /// qual. + /// + /// This abstracts over the differences in between trivial and non-trivial + /// types and allows for one to specify an expansion kind that is passed to + /// any destroy operations we create if we are asked to assign in non-ossa + /// code. + virtual void emitLoweredStore( + SILBuilder &B, SILLocation loc, SILValue value, SILValue addr, + StoreOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind expansionKind) const = 0; + //===--------------------------------------------------------------------===// // DestroyValue //===--------------------------------------------------------------------===// diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index b60dafb6f1afc..2ab2715102cbb 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -36,8 +36,13 @@ bool isExclusiveArgument(SILValue V); /// does not look through init/open_existential_addr. bool pointsToLocalObject(SILValue V); -/// Returns true if \p V is a uniquely identified address or reference. It may -/// be any of: +/// Returns true if \p V is a uniquely identified address or reference. Two +/// uniquely identified pointers with distinct roots cannot alias. However, a +/// uniquely identified pointer may alias with unidentified pointers. For +/// example, the uniquely identified pointer may escape to a call that returns an +/// alias of that pointer. +/// +/// It may be any of: /// /// - an address projection based on a locally allocated address with no /// indirection diff --git a/include/swift/SILOptimizer/Differentiation/ADContext.h b/include/swift/SILOptimizer/Differentiation/ADContext.h index 4fc2656ad404e..368d7fc8d4aec 100644 --- a/include/swift/SILOptimizer/Differentiation/ADContext.h +++ b/include/swift/SILOptimizer/Differentiation/ADContext.h @@ -73,6 +73,9 @@ class ADContext { llvm::SmallVector differentiableFunctionInsts; + /// The worklist (stack) of `linear_function` instructions to be processed. + llvm::SmallVector linearFunctionInsts; + /// The set of `differentiable_function` instructions that have been /// processed. Used to avoid reprocessing invalidated instructions. /// NOTE(TF-784): if we use `CanonicalizeInstruction` subclass to replace @@ -80,6 +83,12 @@ class ADContext { llvm::SmallPtrSet processedDifferentiableFunctionInsts; + /// The set of `linear_function` instructions that have been processed. Used + /// to avoid reprocessing invalidated instructions. + /// NOTE(TF-784): if we use `CanonicalizeInstruction` subclass to replace + /// `ADContext::processLinearFunctionInst`, this field may be removed. + llvm::SmallPtrSet processedLinearFunctionInsts; + /// Mapping from witnesses to invokers. /// `SmallMapVector` is used for deterministic insertion order iteration. llvm::SmallMapVector & + getDifferentiableFunctionInstWorklist() { + return differentiableFunctionInsts; } - /// Pops and returns a `differentiable_function` instruction from the - /// worklist. Returns nullptr if the worklist is empty. - DifferentiableFunctionInst *popDifferentiableFunctionInstFromWorklist() { - if (differentiableFunctionInsts.empty()) - return nullptr; - return differentiableFunctionInsts.pop_back_val(); + llvm::SmallVectorImpl &getLinearFunctionInstWorklist() { + return linearFunctionInsts; } - /// Adds the given `differentiable_function` instruction to the worklist. - void - addDifferentiableFunctionInstToWorklist(DifferentiableFunctionInst *dfi) { - differentiableFunctionInsts.push_back(dfi); - } + /// Get or create the synthesized file for the given `SILFunction`. + /// Used by `LinearMapInfo` for adding generated linear map struct and + /// branching trace enum declarations. + SynthesizedFileUnit &getOrCreateSynthesizedFile(SILFunction *original); /// Returns true if the given `differentiable_function` instruction has /// already been processed. @@ -159,6 +157,17 @@ class ADContext { processedDifferentiableFunctionInsts.insert(dfi); } + /// Returns true if the given `linear_function` instruction has already been + /// processed. + bool isLinearFunctionInstProcessed(LinearFunctionInst *lfi) const { + return processedLinearFunctionInsts.count(lfi); + } + + /// Adds the given `linear_function` instruction to the worklist. + void markLinearFunctionInstAsProcessed(LinearFunctionInst *lfi) { + processedLinearFunctionInsts.insert(lfi); + } + const llvm::SmallMapVector & getInvokers() const { @@ -204,12 +213,26 @@ class ADContext { IndexSubset *resultIndices, SILValue original, Optional> derivativeFunctions = None); - // Given an `differentiable_function` instruction, finds the corresponding + /// Creates a `linear_function` instruction using the given builder + /// and arguments. Erase the newly created instruction from the processed set, + /// if it exists - it may exist in the processed set if it has the same + /// pointer value as a previously processed and deleted instruction. + LinearFunctionInst * + createLinearFunction(SILBuilder &builder, SILLocation loc, + IndexSubset *parameterIndices, SILValue original, + Optional transposeFunction = None); + + // Given a `differentiable_function` instruction, finds the corresponding // differential operator used in the AST. If no differential operator is // found, return nullptr. DifferentiableFunctionExpr * findDifferentialOperator(DifferentiableFunctionInst *inst); + // Given a `linear_function` instruction, finds the corresponding differential + // operator used in the AST. If no differential operator is found, return + // nullptr. + LinearFunctionExpr *findDifferentialOperator(LinearFunctionInst *inst); + template InFlightDiagnostic diagnose(SourceLoc loc, Diag diag, U &&... args) const { @@ -300,6 +323,21 @@ ADContext::emitNondifferentiabilityError(SourceLoc loc, return diagnose(loc, diag, std::forward(args)...); } + // For `linear_function` instructions: if the `linear_function` instruction + // comes from a differential operator, emit an error on the expression and a + // note on the non-differentiable operation. Otherwise, emit both an error and + // note on the non-differentiation operation. + case DifferentiationInvoker::Kind::LinearFunctionInst: { + auto *inst = invoker.getLinearFunctionInst(); + if (auto *expr = findDifferentialOperator(inst)) { + diagnose(expr->getLoc(), diag::autodiff_function_not_differentiable_error) + .highlight(expr->getSubExpr()->getSourceRange()); + return diagnose(loc, diag, std::forward(args)...); + } + diagnose(loc, diag::autodiff_expression_not_differentiable_error); + return diagnose(loc, diag, std::forward(args)...); + } + // For differentiability witnesses: try to find a `@differentiable` or // `@derivative` attribute. If an attribute is found, emit an error on it; // otherwise, emit an error on the original function. diff --git a/include/swift/SILOptimizer/Differentiation/DifferentiationInvoker.h b/include/swift/SILOptimizer/Differentiation/DifferentiationInvoker.h index 405fcc15a97f7..63b9eb6eb824b 100644 --- a/include/swift/SILOptimizer/Differentiation/DifferentiationInvoker.h +++ b/include/swift/SILOptimizer/Differentiation/DifferentiationInvoker.h @@ -25,6 +25,7 @@ namespace swift { class ApplyInst; class DifferentiableFunctionInst; +class LinearFunctionInst; class SILDifferentiabilityWitness; namespace autodiff { @@ -42,6 +43,10 @@ struct DifferentiationInvoker { // expression). DifferentiableFunctionInst, + // Invoked by an `linear_function` instruction, which may or may not + // be linked to a Swift AST node (e.g. an `LinearFunctionExpr` expression). + LinearFunctionInst, + // Invoked by the indirect application of differentiation. This case has an // associated original `apply` instruction and // `SILDifferentiabilityWitness`. @@ -60,6 +65,10 @@ struct DifferentiationInvoker { DifferentiableFunctionInst *diffFuncInst; Value(DifferentiableFunctionInst *inst) : diffFuncInst(inst) {} + /// The instruction associated with the `LinearFunctionInst` case. + LinearFunctionInst *linearFuncInst; + Value(LinearFunctionInst *inst) : linearFuncInst(inst) {} + /// The parent `apply` instruction and the witness associated with the /// `IndirectDifferentiation` case. std::pair @@ -79,6 +88,8 @@ struct DifferentiationInvoker { public: DifferentiationInvoker(DifferentiableFunctionInst *inst) : kind(Kind::DifferentiableFunctionInst), value(inst) {} + DifferentiationInvoker(LinearFunctionInst *inst) + : kind(Kind::LinearFunctionInst), value(inst) {} DifferentiationInvoker(ApplyInst *applyInst, SILDifferentiabilityWitness *witness) : kind(Kind::IndirectDifferentiation), value({applyInst, witness}) {} @@ -92,6 +103,11 @@ struct DifferentiationInvoker { return value.diffFuncInst; } + LinearFunctionInst *getLinearFunctionInst() const { + assert(kind == Kind::LinearFunctionInst); + return value.linearFuncInst; + } + std::pair getIndirectDifferentiation() const { assert(kind == Kind::IndirectDifferentiation); diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 82f18120c6038..4d41b442d5b9a 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -68,8 +68,10 @@ PASS(CrossModuleSerializationSetup, "cross-module-serialization-setup", "Setup serialization flags for cross-module optimization") PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") +PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump", + "Dump Accessed Storage Analysis Summaries") PASS(AccessedStorageDumper, "accessed-storage-dump", - "Dump Accessed Storage Summary") + "Dump Accessed Storage Information") PASS(AccessMarkerElimination, "access-marker-elim", "Access Marker Elimination.") PASS(AddressLowering, "address-lowering", @@ -297,6 +299,8 @@ PASS(SILCombine, "sil-combine", "Combine SIL Instructions via Peephole Optimization") PASS(SILDebugInfoGenerator, "sil-debuginfo-gen", "Generate Debug Information with Source Locations into Textual SIL") +PASS(EarlySROA, "early-sroa", + "Scalar Replacement of Aggregate Stack Objects on high-level SIL") PASS(SROA, "sroa", "Scalar Replacement of Aggregate Stack Objects") PASS(SROABBArgs, "sroa-bb-args", @@ -315,6 +319,8 @@ PASS(StackPromotion, "stack-promotion", "Stack Promotion of Class Objects") PASS(StripDebugInfo, "strip-debug-info", "Strip Debug Information") +PASS(StringOptimization, "string-optimization", + "Optimization for String operations") PASS(SwiftArrayPropertyOpt, "array-property-opt", "Loop Specialization for Array Properties") PASS(UnsafeGuaranteedPeephole, "unsafe-guaranteed-peephole", @@ -345,6 +351,8 @@ PASS(MandatoryCombine, "mandatory-combine", "Perform mandatory peephole combines") PASS(BugReducerTester, "bug-reducer-tester", "sil-bug-reducer Tool Testing by Asserting on a Sentinel Function") +PASS(OptRemarkGenerator, "sil-opt-remark-generator", + "Emit small peephole opt remarks that do not use large analyses") PASS(PruneVTables, "prune-vtables", "Mark class methods that do not require vtable dispatch") PASS_RANGE(AllPasses, AADumper, PruneVTables) diff --git a/include/swift/SILOptimizer/Utils/ConstantFolding.h b/include/swift/SILOptimizer/Utils/ConstantFolding.h index 9fd5dc883d665..0aadf1adb580c 100644 --- a/include/swift/SILOptimizer/Utils/ConstantFolding.h +++ b/include/swift/SILOptimizer/Utils/ConstantFolding.h @@ -70,8 +70,6 @@ class ConstantFolder { /// Called for each constant folded instruction. std::function Callback; - bool constantFoldStringConcatenation(ApplyInst *AI); - public: /// The constructor. /// diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index 994ff78240b36..522abb0f5a68c 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -226,6 +226,36 @@ void eraseUsesOfInstruction( /// value itself) void eraseUsesOfValue(SILValue value); +/// Gets the concrete value which is stored in an existential box. +/// Returns %value in following pattern: +/// +/// %existentialBox = alloc_existential_box $Error, $ConcreteError +/// %a = project_existential_box $ConcreteError in %existentialBox : $Error +/// store %value to %a : $*ConcreteError +/// +/// Returns an invalid SILValue in case there are multiple stores or any unknown +/// users of \p existentialBox. +/// The \p ignoreUser is ignored in the user list of \p existentialBox. +SILValue +getConcreteValueOfExistentialBox(AllocExistentialBoxInst *existentialBox, + SILInstruction *ignoreUser); + +/// Gets the concrete value which is stored in an existential box, which itself +/// is stored in \p addr. +/// Returns %value in following pattern: +/// +/// %b = alloc_existential_box $Error, $ConcreteError +/// %a = project_existential_box $ConcreteError in %b : $Error +/// store %value to %a : $*ConcreteError +/// %addr = alloc_stack $Error +/// store %b to %addr : $*Error +/// +/// Returns an invalid SILValue in case there are multiple stores or any unknown +/// users of \p addr or the existential box. +/// The \p ignoreUser is ignored in the user list of \p addr. +SILValue getConcreteValueOfExistentialBoxAddr(SILValue addr, + SILInstruction *ignoreUser); + FullApplySite findApplyFromDevirtualizedResult(SILValue value); /// Cast a value into the expected, ABI compatible type if necessary. @@ -269,11 +299,6 @@ TermInst *addArgumentToBranch(SILValue val, SILBasicBlock *dest, /// the given linkage. SILLinkage getSpecializedLinkage(SILFunction *f, SILLinkage linkage); -/// Tries to optimize a given apply instruction if it is a concatenation of -/// string literals. Returns a new instruction if optimization was possible. -SingleValueInstruction *tryToConcatenateStrings(ApplyInst *ai, - SILBuilder &builder); - /// Tries to perform jump-threading on all checked_cast_br instruction in /// function \p Fn. bool tryCheckedCastBrJumpThreading( @@ -497,11 +522,7 @@ bool calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl); /// Do we have enough information to determine all callees that could /// be reached by calling the function represented by Decl? -bool calleesAreStaticallyKnowable(SILModule &module, AbstractFunctionDecl *afd); - -/// Do we have enough information to determine all callees that could -/// be reached by calling the function represented by Decl? -bool calleesAreStaticallyKnowable(SILModule &module, EnumElementDecl *eed); +bool calleesAreStaticallyKnowable(SILModule &module, ValueDecl *vd); // Attempt to get the instance for , whose static type is the same as // its exact dynamic type, returning a null SILValue() if we cannot find it. diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index da3a8b269b702..afb51d61b70f9 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -133,8 +133,7 @@ namespace swift { bool typeCheckExpression(DeclContext *DC, Expr *&parsedExpr); /// Type check a function body element which is at \p TagetLoc . - bool typeCheckAbstractFunctionBodyAtLoc(AbstractFunctionDecl *AFD, - SourceLoc TargetLoc); + bool typeCheckASTNodeAtLoc(DeclContext *DC, SourceLoc TargetLoc); /// Typecheck top-level code parsed during code completion. /// diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 0eb1690aa6ed7..ec2ad4a8a29ab 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -51,8 +51,9 @@ struct SerializedModuleBaseName { std::string getName(file_types::ID fileTy) const; }; -/// Common functionality shared between \c SerializedModuleLoader, -/// \c ModuleInterfaceLoader and \c MemoryBufferSerializedModuleLoader. +/// Common functionality shared between \c ImplicitSerializedModuleLoader, +/// \c ModuleInterfaceLoader, \c ExplicitSwiftModuleLoader +/// and \c MemoryBufferSerializedModuleLoader. class SerializedModuleLoaderBase : public ModuleLoader { /// A { module, generation # } pair. using LoadedModulePair = std::pair, unsigned>; @@ -201,16 +202,23 @@ class SerializedModuleLoaderBase : public ModuleLoader { StringRef moduleName, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) override; - virtual std::string getUpToDateCompiledModuleForInterface(StringRef moduleName, - StringRef interfacePath) { - return std::string(); + virtual std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) { + return std::vector(); + } + virtual bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) { + return false; } }; /// Imports serialized Swift modules into an ASTContext. -class SerializedModuleLoader : public SerializedModuleLoaderBase { +class ImplicitSerializedModuleLoader : public SerializedModuleLoaderBase { - SerializedModuleLoader(ASTContext &ctx, DependencyTracker *tracker, + ImplicitSerializedModuleLoader(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, bool IgnoreSwiftSourceInfo) : SerializedModuleLoaderBase(ctx, tracker, loadMode, IgnoreSwiftSourceInfo) {} @@ -229,7 +237,7 @@ class SerializedModuleLoader : public SerializedModuleLoaderBase { const SerializedModuleBaseName &BaseName) override; public: - virtual ~SerializedModuleLoader(); + virtual ~ImplicitSerializedModuleLoader(); /// Append visible module names to \p names. Note that names are possibly /// duplicated, and not guaranteed to be ordered in any way. @@ -238,12 +246,12 @@ class SerializedModuleLoader : public SerializedModuleLoaderBase { /// Create a new importer that can load serialized Swift modules /// into the given ASTContext. - static std::unique_ptr + static std::unique_ptr create(ASTContext &ctx, DependencyTracker *tracker = nullptr, ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized, bool IgnoreSwiftSourceInfo = false) { - return std::unique_ptr{ - new SerializedModuleLoader(ctx, tracker, loadMode, IgnoreSwiftSourceInfo) + return std::unique_ptr{ + new ImplicitSerializedModuleLoader(ctx, tracker, loadMode, IgnoreSwiftSourceInfo) }; } }; diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index c727b198cc0de..f0e84e7d7676e 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -172,7 +172,7 @@ ValidationInfo validateSerializedAST( /// Status::Valid. /// - \p moduleBufferID and \p moduleDocBufferID are the buffer identifiers /// of the module input and doc input buffers respectively (\ref -/// SerializedModuleLoader::loadAST, \ref ModuleFile::load). +/// SerializedModuleLoaderBase::loadAST, \ref ModuleFile::load). /// - \p loadedModuleFile is an invalid loaded module. /// - \p ModuleName is the name used to refer to the module in diagnostics. void diagnoseSerializedASTLoadFailure( diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 09f746dfe1d7e..0a99b5f92175a 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -62,10 +62,12 @@ namespace swift { class SourceManager; class SyntaxParseActions; class SyntaxParsingCache; + struct TBDGenOptions; class Token; class TopLevelContext; + class Type; class TypeCheckerOptions; - class TypeLoc; + class TypeRepr; class UnifiedStatsReporter; namespace Lowering { @@ -139,28 +141,15 @@ namespace swift { /// emitted. void performWholeModuleTypeChecking(SourceFile &SF); - /// Recursively validate the specified type. + /// Resolve the given \c TypeRepr to a contextual type. /// /// This is used when dealing with partial source files (e.g. SIL parsing, /// code completion). /// - /// \returns false on success, true on error. - bool performTypeLocChecking(ASTContext &Ctx, TypeLoc &T, - DeclContext *DC, - bool ProduceDiagnostics = true); - - /// Recursively validate the specified type. - /// - /// This is used when dealing with partial source files (e.g. SIL parsing, - /// code completion). - /// - /// \returns false on success, true on error. - bool performTypeLocChecking(ASTContext &Ctx, TypeLoc &T, - bool isSILMode, - bool isSILType, - GenericEnvironment *GenericEnv, - DeclContext *DC, - bool ProduceDiagnostics = true); + /// \returns A well-formed type on success, or an \c ErrorType. + Type performTypeResolution(TypeRepr *TyR, ASTContext &Ctx, bool isSILMode, + bool isSILType, GenericEnvironment *GenericEnv, + DeclContext *DC, bool ProduceDiagnostics = true); /// Expose TypeChecker's handling of GenericParamList to SIL parsing. GenericEnvironment *handleSILGenericParams(GenericParamList *genericParams, @@ -203,27 +192,25 @@ namespace swift { std::string> getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx); - /// Turn the given Swift module into either LLVM IR or native code - /// and return the generated LLVM IR module. - /// If you set an outModuleHash, then you need to call performLLVM. + /// Turn the given Swift module into LLVM IR and return the generated module. + /// To compile and output the generated code, call \c performLLVM. GeneratedModule - performIRGeneration(const IRGenOptions &Opts, ModuleDecl *M, + performIRGeneration(ModuleDecl *M, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash = nullptr, - llvm::StringSet<> *LinkerDirectives = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr); - /// Turn the given Swift module into either LLVM IR or native code - /// and return the generated LLVM IR module. - /// If you set an outModuleHash, then you need to call performLLVM. + /// Turn the given Swift file into LLVM IR and return the generated module. + /// To compile and output the generated code, call \c performLLVM. GeneratedModule - performIRGeneration(const IRGenOptions &Opts, SourceFile &SF, + performIRGeneration(FileUnit *file, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, StringRef PrivateDiscriminator, - llvm::GlobalVariable **outModuleHash = nullptr, - llvm::StringSet<> *LinkerDirectives = nullptr); + llvm::GlobalVariable **outModuleHash = 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 @@ -231,6 +218,15 @@ namespace swift { void performLLVMOptimizations(const IRGenOptions &Opts, llvm::Module *Module, llvm::TargetMachine *TargetMachine); + /// Compiles and writes the given LLVM module into an output stream in the + /// format specified in the \c IRGenOptions. + bool compileAndWriteLLVM(llvm::Module *module, + llvm::TargetMachine *targetMachine, + const IRGenOptions &opts, + UnifiedStatsReporter *stats, DiagnosticEngine &diags, + llvm::raw_pwrite_stream &out, + llvm::sys::Mutex *diagMutex = nullptr); + /// Wrap a serialized module inside a swift AST section in an object file. void createSwiftModuleObjectFile(SILModule &SILMod, StringRef Buffer, StringRef OutputPath); @@ -250,7 +246,6 @@ namespace swift { /// was already compiled, may be null if not desired. /// \param Module LLVM module to code gen, required. /// \param TargetMachine target of code gen, required. - /// \param effectiveLanguageVersion version of the language, effectively. /// \param OutputFilename Filename for output. bool performLLVM(const IRGenOptions &Opts, DiagnosticEngine &Diags, @@ -258,7 +253,6 @@ namespace swift { llvm::GlobalVariable *HashGlobal, llvm::Module *Module, llvm::TargetMachine *TargetMachine, - const version::Version &effectiveLanguageVersion, StringRef OutputFilename, UnifiedStatsReporter *Stats); diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h index 7394547bef39c..08f8c3cca83ca 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h @@ -80,6 +80,11 @@ void swift_reflection_addReflectionInfo(SwiftReflectionContextRef ContextRef, swift_reflection_info_t Info); +/// Add reflection sections from a loaded Swift image. +SWIFT_REMOTE_MIRROR_LINKAGE +void swift_reflection_addReflectionMappingInfo( + SwiftReflectionContextRef ContextRef, swift_reflection_mapping_info_t Info); + /// Add reflection information from a loaded Swift image. /// Returns true on success, false if the image's memory couldn't be read. SWIFT_REMOTE_MIRROR_LINKAGE @@ -339,6 +344,12 @@ const char * swift_reflection_metadataAllocationTagName(SwiftReflectionContextRef ContextRef, swift_metadata_allocation_tag_t Tag); +SWIFT_REMOTE_MIRROR_LINKAGE +int swift_reflection_metadataAllocationCacheNode( + SwiftReflectionContextRef ContextRef, + swift_metadata_allocation_t Allocation, + swift_metadata_cache_node_t *OutNode); + /// Backtrace iterator callback passed to /// swift_reflection_iterateMetadataAllocationBacktraces typedef void (*swift_metadataAllocationBacktraceIterator)( diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h index 73ff685ac0e19..13331659e805c 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h @@ -48,11 +48,23 @@ typedef struct swift_reflection_section { void *End; } swift_reflection_section_t; +/// Represents the remote address and size of an image's section +typedef struct swift_remote_reflection_section { + uintptr_t StartAddress; + uintptr_t Size; +} swift_remote_reflection_section_t; + typedef struct swift_reflection_section_pair { swift_reflection_section_t section; swift_reflection_ptr_t offset; ///< DEPRECATED. Must be zero } swift_reflection_section_pair_t; +/// Represents the mapping between an image sections's local and remote addresses +typedef struct swift_reflection_section_mapping { + swift_reflection_section_t local_section; + swift_remote_reflection_section_t remote_section; +} swift_reflection_section_mapping_t; + /// Represents the set of Swift reflection sections of an image. /// Not all sections may be present. /// @@ -71,6 +83,16 @@ typedef struct swift_reflection_info { swift_reflection_ptr_t RemoteStartAddress; } swift_reflection_info_t; +/// Represents the set of Swift reflection sections of an image, +typedef struct swift_reflection_mapping_info { + swift_reflection_section_mapping_t field; + swift_reflection_section_mapping_t associated_types; + swift_reflection_section_mapping_t builtin_types; + swift_reflection_section_mapping_t capture; + swift_reflection_section_mapping_t type_references; + swift_reflection_section_mapping_t reflection_strings; +} swift_reflection_mapping_info_t; + /// The layout kind of a Swift type. typedef enum swift_layout_kind { // Nothing is known about the size or contents of this value. @@ -162,6 +184,11 @@ typedef struct swift_metadata_allocation { unsigned Size; } swift_metadata_allocation_t; +typedef struct swift_metadata_cache_node { + swift_reflection_ptr_t Left; + swift_reflection_ptr_t Right; +} swift_metadata_cache_node_t; + /// An opaque pointer to a context which maintains state and /// caching of reflection structure for heap instances. typedef struct SwiftReflectionContext *SwiftReflectionContextRef; diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index 1da2386d3132a..72229a63707cc 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -14,6 +14,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringSet.h" +#include "swift/AST/TBDGenRequests.h" #include "swift/Basic/Version.h" #include @@ -29,10 +30,10 @@ class ModuleDecl; /// output. struct TBDGenOptions { /// Whether this compilation has multiple IRGen instances. - bool HasMultipleIGMs; + bool HasMultipleIGMs = false; /// Whether this compilation is producing a TBD for InstallAPI. - bool IsInstallAPI; + bool IsInstallAPI = false; /// Only collect linker directive symbols. bool LinkerDirectivesOnly = false; @@ -88,10 +89,7 @@ struct TBDGenOptions { } }; -void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols, - const TBDGenOptions &opts); -void enumeratePublicSymbols(ModuleDecl *module, llvm::StringSet<> &symbols, - const TBDGenOptions &opts); +std::vector getPublicSymbols(TBDGenDescriptor desc); void writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os, const TBDGenOptions &opts); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index bf3b609c68d30..f95561eb4befd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -40,6 +40,7 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/SILLayout.h" @@ -240,6 +241,9 @@ struct ASTContext::Implementation { /// func append(Element) -> void FuncDecl *ArrayAppendElementDecl = nullptr; + /// init(Builtin.RawPointer, Builtin.Word, Builtin.Int1) + ConstructorDecl *MakeUTF8StringDecl = nullptr; + /// func reserveCapacityForAppend(newElementsCount: Int) FuncDecl *ArrayReserveCapacityDecl = nullptr; @@ -768,7 +772,7 @@ FuncDecl *ASTContext::getSequenceMakeIterator() const { CanType ASTContext::getExceptionType() const { if (auto exn = getErrorDecl()) { - return exn->getDeclaredType()->getCanonicalType(); + return exn->getDeclaredInterfaceType()->getCanonicalType(); } else { // Use Builtin.NativeObject just as a stand-in. return TheNativeObjectType; @@ -860,7 +864,7 @@ CanType ASTContext::getNeverType() const { auto neverDecl = getNeverDecl(); if (!neverDecl) return CanType(); - return neverDecl->getDeclaredType()->getCanonicalType(); + return neverDecl->getDeclaredInterfaceType()->getCanonicalType(); } #define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECLTYPE) \ @@ -1069,7 +1073,7 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, if (witness) return witness; - auto type = decl->getDeclaredType(); + auto type = decl->getDeclaredInterfaceType(); auto builtinProtocol = getProtocol(builtinProtocolKind); auto builtinConformance = getStdlibModule()->lookupConformance( type, builtinProtocol); @@ -1098,12 +1102,12 @@ FuncDecl *getBinaryComparisonOperatorIntDecl(const ASTContext &C, StringRef op, if (!C.getIntDecl() || !C.getBoolDecl()) return nullptr; - auto intType = C.getIntDecl()->getDeclaredType(); + auto intType = C.getIntDecl()->getDeclaredInterfaceType(); auto isIntParam = [&](AnyFunctionType::Param param) { return (!param.isVariadic() && !param.isInOut() && param.getPlainType()->isEqual(intType)); }; - auto boolType = C.getBoolDecl()->getDeclaredType(); + auto boolType = C.getBoolDecl()->getDeclaredInterfaceType(); auto decl = lookupOperatorFunc(C, op, intType, [=](FunctionType *type) { // Check for the signature: (Int, Int) -> Bool @@ -1247,6 +1251,33 @@ FuncDecl *ASTContext::getArrayReserveCapacityDecl() const { return nullptr; } +ConstructorDecl *ASTContext::getMakeUTF8StringDecl() const { + if (getImpl().MakeUTF8StringDecl) + return getImpl().MakeUTF8StringDecl; + + auto initializers = + getStringDecl()->lookupDirect(DeclBaseName::createConstructor()); + + for (Decl *initializer : initializers) { + auto *constructor = cast(initializer); + auto Attrs = constructor->getAttrs(); + for (auto *A : Attrs.getAttributes()) { + if (A->Value != semantics::STRING_MAKE_UTF8) + continue; + auto ParamList = constructor->getParameters(); + if (ParamList->size() != 3) + continue; + ParamDecl *param = constructor->getParameters()->get(0); + if (param->getArgumentName().str() != "_builtinStringLiteral") + continue; + + getImpl().MakeUTF8StringDecl = constructor; + return constructor; + } + } + return nullptr; +} + FuncDecl *ASTContext::getIsOSVersionAtLeastDecl() const { if (getImpl().IsOSVersionAtLeastDecl) return getImpl().IsOSVersionAtLeastDecl; @@ -2416,10 +2447,9 @@ Type TupleType::get(ArrayRef Fields, const ASTContext &C) { } } + size_t bytes = totalSizeToAlloc(Fields.size()); // TupleType will copy the fields list into ASTContext owned memory. - void *mem = C.Allocate(sizeof(TupleType) + - sizeof(TupleTypeElt) * Fields.size(), - alignof(TupleType), arena); + void *mem = C.Allocate(bytes, alignof(TupleType), arena); auto New = new (mem) TupleType(Fields, IsCanonical ? &C : nullptr, properties, hasElementWithOwnership); C.getImpl().getArena(arena).TupleTypes.InsertNode(New, InsertPos); @@ -2622,7 +2652,7 @@ BoundGenericType *BoundGenericType::get(NominalTypeDecl *TheDecl, newType = new (mem) BoundGenericClassType( theClass, Parent, GenericArgs, IsCanonical ? &C : nullptr, properties); } else if (auto theStruct = dyn_cast(TheDecl)) { - auto sz =BoundGenericStructType::totalSizeToAlloc(GenericArgs.size()); + auto sz = BoundGenericStructType::totalSizeToAlloc(GenericArgs.size()); auto mem = C.Allocate(sz, alignof(BoundGenericStructType), arena); newType = new (mem) BoundGenericStructType( theStruct, Parent, GenericArgs, IsCanonical ? &C : nullptr, properties); @@ -3082,17 +3112,16 @@ FunctionType *FunctionType::get(ArrayRef params, return funcTy; } - Optional uncommon = info.getUncommonInfo(); + Optional clangTypeInfo = info.getClangTypeInfo(); - size_t allocSize = - totalSizeToAlloc( - params.size(), uncommon.hasValue() ? 1 : 0); + size_t allocSize = totalSizeToAlloc( + params.size(), clangTypeInfo.hasValue() ? 1 : 0); void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isFunctionTypeCanonical(params, result); - if (uncommon.hasValue()) { + if (clangTypeInfo.hasValue()) { if (ctx.LangOpts.UseClangFunctionTypes) - isCanonical &= uncommon->ClangFunctionType->isCanonicalUnqualified(); + isCanonical &= clangTypeInfo->type->isCanonicalUnqualified(); else isCanonical = false; } @@ -3113,9 +3142,9 @@ FunctionType::FunctionType(ArrayRef params, output, properties, params.size(), info) { std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); - Optional uncommon = info.getUncommonInfo(); - if (uncommon.hasValue()) - *getTrailingObjects() = uncommon.getValue(); + auto clangTypeInfo = info.getClangTypeInfo(); + if (clangTypeInfo.hasValue()) + *getTrailingObjects() = clangTypeInfo.getValue(); } void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, @@ -3208,11 +3237,6 @@ 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, @@ -3271,13 +3295,14 @@ SILFunctionType::SILFunctionType( WitnessMethodConformance(witnessMethodConformance) { Bits.SILFunctionType.HasErrorResult = errorResult.hasValue(); - Bits.SILFunctionType.ExtInfoBits = ext.Bits; - Bits.SILFunctionType.HasUncommonInfo = false; + Bits.SILFunctionType.ExtInfoBits = ext.getBits(); + Bits.SILFunctionType.HasClangTypeInfo = false; Bits.SILFunctionType.HasPatternSubs = (bool) patternSubs; Bits.SILFunctionType.HasInvocationSubs = (bool) invocationSubs; // The use of both assert() and static_assert() below is intentional. - assert(Bits.SILFunctionType.ExtInfoBits == ext.Bits && "Bits were dropped!"); - static_assert(ExtInfo::NumMaskBits == NumSILExtInfoBits, + assert(Bits.SILFunctionType.ExtInfoBits == ext.getBits() && + "Bits were dropped!"); + static_assert(SILExtInfoBuilder::NumMaskBits == NumSILExtInfoBits, "ExtInfo and SILFunctionTypeBitfields must agree on bit size"); Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind); NumParameters = params.size(); @@ -3453,7 +3478,7 @@ CanSILFunctionType SILFunctionType::get( // All SILFunctionTypes are canonical. - // See [SILFunctionType-layout] + // See [NOTE: SILFunctionType-layout] bool hasResultCache = normalResults.size() > 1; size_t bytes = totalSizeToAllocgetParentModule()->lookupConformance( - nominal->getDeclaredType(), objcBridgeable); + nominal->getDeclaredInterfaceType(), objcBridgeable); if (conformance) { result = ForeignRepresentationInfo::forBridged(conformance.getConcrete()); @@ -4415,7 +4440,6 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, const clang::Type * ASTContext::getClangFunctionType(ArrayRef params, Type resultTy, - FunctionType::ExtInfo incompleteExtInfo, FunctionTypeRepresentation trueRep) { auto &impl = getImpl(); if (!impl.Converter) { diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 57692d6be94cf..44d32c84a7a7d 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -153,7 +153,7 @@ Type ASTBuilder::createNominalType(GenericTypeDecl *decl, Type parent) { bool isImported = nominalDecl->hasClangNode() || nominalDecl->getAttrs().hasAttribute(); if (isImported && !nominalDecl->isGenericContext()) - return nominalDecl->getDeclaredType(); + return nominalDecl->getDeclaredInterfaceType(); // Validate the parent type. if (!validateParentType(nominalDecl, parent)) @@ -323,12 +323,7 @@ Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, return aliasDecl->getDeclaredInterfaceType().subst(subMap); } -Type ASTBuilder::createTupleType(ArrayRef eltTypes, - StringRef labels, - bool isVariadic) { - // Just bail out on variadic tuples for now. - if (isVariadic) return Type(); - +Type ASTBuilder::createTupleType(ArrayRef eltTypes, StringRef labels) { SmallVector elements; elements.reserve(eltTypes.size()); for (auto eltType : eltTypes) { @@ -405,18 +400,16 @@ Type ASTBuilder::createFunctionType( || representation == FunctionTypeRepresentation::Block) && !flags.isEscaping(); - FunctionType::ExtInfo incompleteExtInfo( - FunctionTypeRepresentation::Swift, - noescape, flags.throws(), diffKind, /*clangFunctionType*/nullptr); - const clang::Type *clangFunctionType = nullptr; if (representation == FunctionTypeRepresentation::CFunctionPointer) clangFunctionType = Ctx.getClangFunctionType(funcParams, output, - incompleteExtInfo, representation); - auto einfo = incompleteExtInfo.withRepresentation(representation) - .withClangFunctionType(clangFunctionType); + auto einfo = + FunctionType::ExtInfoBuilder(representation, noescape, flags.isThrowing(), + diffKind, clangFunctionType) + .withAsync(flags.isAsync()) + .build(); return FunctionType::get(funcParams, output, einfo); } @@ -535,10 +528,11 @@ Type ASTBuilder::createImplFunctionType( break; } - // TODO: [store-sil-clang-function-type] - auto einfo = SILFunctionType::ExtInfo(representation, flags.isPseudogeneric(), - !flags.isEscaping(), diffKind, - /*clangFunctionType*/ nullptr); + // [TODO: Store-SIL-Clang-type] + auto einfo = SILExtInfoBuilder(representation, flags.isPseudogeneric(), + !flags.isEscaping(), diffKind, + /*clangFunctionType*/ nullptr) + .build(); llvm::SmallVector funcParams; llvm::SmallVector funcYields; @@ -576,7 +570,7 @@ Type ASTBuilder::createProtocolCompositionType( bool isClassBound) { std::vector members; for (auto protocol : protocols) - members.push_back(protocol->getDeclaredType()); + members.push_back(protocol->getDeclaredInterfaceType()); if (superclass && superclass->getClassOrBoundGenericClass()) members.push_back(superclass); return ProtocolCompositionType::get(Ctx, members, isClassBound); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index df7b9e300fc23..532790cc94f3b 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -323,13 +323,10 @@ getForeignErrorConventionKindString(ForeignErrorConvention::Kind value) { static StringRef getDefaultArgumentKindString(DefaultArgumentKind value) { switch (value) { case DefaultArgumentKind::None: return "none"; - case DefaultArgumentKind::Column: return "#column"; - case DefaultArgumentKind::DSOHandle: return "#dsohandle"; - case DefaultArgumentKind::File: return "#file"; - case DefaultArgumentKind::FilePath: return "#filePath"; - case DefaultArgumentKind::Function: return "#function"; +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: return STRING; +#include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::Inherited: return "inherited"; - case DefaultArgumentKind::Line: return "#line"; case DefaultArgumentKind::NilLiteral: return "nil"; case DefaultArgumentKind::EmptyArray: return "[]"; case DefaultArgumentKind::EmptyDictionary: return "[:]"; @@ -371,7 +368,6 @@ static StringRef getStringLiteralExprEncodingString(StringLiteralExpr::Encoding value) { switch (value) { case StringLiteralExpr::UTF8: return "utf8"; - case StringLiteralExpr::UTF16: return "utf16"; case StringLiteralExpr::OneUnicodeScalar: return "unicodeScalar"; } @@ -507,7 +503,7 @@ namespace { printRec(P->getSubExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void visitVarPattern(VarPattern *P) { + void visitBindingPattern(BindingPattern *P) { printCommon(P, P->isLet() ? "pattern_let" : "pattern_var"); OS << '\n'; printRec(P->getSubPattern()); @@ -564,8 +560,7 @@ namespace { }; if (const auto GC = Owner.dyn_cast()) { - if (!GC->isGeneric() || isa(GC)) - printWhere(GC->getTrailingWhereClause()); + printWhere(GC->getTrailingWhereClause()); } else { const auto ATD = Owner.get(); printWhere(ATD->getTrailingWhereClause()); @@ -733,9 +728,9 @@ namespace { OS << ' '; printDeclName(VD); if (auto *AFD = dyn_cast(VD)) - printGenericParameters(OS, AFD->getGenericParams()); + printGenericParameters(OS, AFD->getParsedGenericParams()); if (auto *GTD = dyn_cast(VD)) - printGenericParameters(OS, GTD->getGenericParams()); + printGenericParameters(OS, GTD->getParsedGenericParams()); if (auto *var = dyn_cast(VD)) { PrintWithColorRAII(OS, TypeColor) << " type='"; @@ -2134,6 +2129,13 @@ class PrintExpr : public ExprVisitor { printRec(E->getSubExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitAwaitExpr(AwaitExpr *E) { + printCommon(E, "await_expr"); + OS << '\n'; + printRec(E->getSubExpr()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitTupleExpr(TupleExpr *E) { printCommon(E, "tuple_expr"); if (E->hasTrailingClosure()) @@ -2640,7 +2642,12 @@ class PrintExpr : public ExprVisitor { printExplicitCastExpr(E, "coerce_expr"); } void visitArrowExpr(ArrowExpr *E) { - printCommon(E, "arrow") << '\n'; + printCommon(E, "arrow"); + if (E->getAsyncLoc().isValid()) + OS << " async"; + if (E->getThrowsLoc().isValid()) + OS << " throws"; + OS << '\n'; printRec(E->getArgsExpr()); OS << '\n'; printRec(E->getResultExpr()); @@ -2821,6 +2828,11 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, DiscriminatorColor) << "#" << component.getTupleIndex(); break; + case KeyPathExpr::Component::Kind::DictionaryKey: + PrintWithColorRAII(OS, ASTNodeColor) << "dict_key"; + PrintWithColorRAII(OS, IdentifierColor) + << " key='" << component.getUnresolvedDeclName() << "'"; + break; } PrintWithColorRAII(OS, TypeColor) << " type='" << GetTypeOfKeyPathComponent(E, i) << "'"; @@ -2975,7 +2987,9 @@ class PrintTypeRepr : public TypeReprVisitor { void visitFunctionTypeRepr(FunctionTypeRepr *T) { printCommon("type_function"); OS << '\n'; printRec(T->getArgsTypeRepr()); - if (T->throws()) + if (T->isAsync()) + OS << " async "; + if (T->isThrowing()) OS << " throws "; OS << '\n'; printRec(T->getResultTypeRepr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -3745,14 +3759,15 @@ namespace { getSILFunctionTypeRepresentationString(representation)); printFlag(!T->isNoEscape(), "escaping"); - printFlag(T->throws(), "throws"); + printFlag(T->isAsync(), "async"); + printFlag(T->isThrowing(), "throws"); OS << "\n"; Indent += 2; - if (auto *cty = T->getClangFunctionType()) { + if (!T->getClangTypeInfo().empty()) { std::string s; llvm::raw_string_ostream os(s); - cty->dump(os); + T->getClangTypeInfo().dump(os); printField("clang_type", os.str()); } printAnyFunctionParams(T->getParams(), "input"); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 64c84455b14e3..e013ad5db8b3c 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -533,6 +533,12 @@ std::string ASTMangler::mangleTypeForDebugger(Type Ty, const DeclContext *DC) { return finalize(); } +std::string ASTMangler::mangleTypeForTypeName(Type type) { + beginManglingWithoutPrefix(); + appendType(type); + return finalize(); +} + std::string ASTMangler::mangleDeclType(const ValueDecl *decl) { DWARFMangling = true; beginMangling(); @@ -735,23 +741,12 @@ static bool getUnnamedParamIndex(const ParameterList *ParamList, } static unsigned getUnnamedParamIndex(const ParamDecl *D) { - if (auto SD = dyn_cast(D->getDeclContext())) { - unsigned UnnamedIndex = 0; - auto *ParamList = SD->getIndices(); - if (getUnnamedParamIndex(ParamList, D, UnnamedIndex)) - return UnnamedIndex; - llvm_unreachable("param not found"); - } - ParameterList *ParamList; - - if (auto AFD = dyn_cast(D->getDeclContext())) { - ParamList = AFD->getParameters(); - } else if (auto EED = dyn_cast(D->getDeclContext())) { - ParamList = EED->getParameterList(); + auto *DC = D->getDeclContext(); + if (isa(DC)) { + ParamList = cast(DC)->getParameters(); } else { - auto ACE = cast(D->getDeclContext()); - ParamList = ACE->getParameters(); + ParamList = getParameterList(cast(DC->getAsDecl())); } unsigned UnnamedIndex = 0; @@ -1834,7 +1829,7 @@ namespace { VarDecl *visitParenPattern(ParenPattern *P) { return visit(P->getSubPattern()); } - VarDecl *visitVarPattern(VarPattern *P) { + VarDecl *visitBindingPattern(BindingPattern *P) { return visit(P->getSubPattern()); } VarDecl *visitTypedPattern(TypedPattern *P) { @@ -2277,7 +2272,9 @@ void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, const ValueDecl *forDecl) { appendFunctionResultType(fn->getResult(), forDecl); appendFunctionInputType(fn->getParams(), forDecl); - if (fn->throws()) + if (fn->isAsync()) + appendOperator("Y"); + if (fn->isThrowing()) appendOperator("K"); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 5d0cefc797327..49305518234fc 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -1079,7 +1079,9 @@ void PrintAST::printTypedPattern(const TypedPattern *TP) { if (auto decl = named->getDecl()) isIUO = decl->isImplicitlyUnwrappedOptional(); - printTypeLocForImplicitlyUnwrappedOptional(TP->getTypeLoc(), isIUO); + const auto TyLoc = TypeLoc(TP->getTypeRepr(), + TP->hasType() ? TP->getType() : Type()); + printTypeLocForImplicitlyUnwrappedOptional(TyLoc, isIUO); } /// Determines if we are required to print the name of a property declaration, @@ -1192,12 +1194,12 @@ void PrintAST::printPattern(const Pattern *pattern) { // FIXME: Print expr. break; - case PatternKind::Var: + case PatternKind::Binding: if (!Options.SkipIntroducerKeywords) - Printer << (cast(pattern)->isLet() ? tok::kw_let - : tok::kw_var) + Printer << (cast(pattern)->isLet() ? tok::kw_let + : tok::kw_var) << " "; - printPattern(cast(pattern)->getSubPattern()); + printPattern(cast(pattern)->getSubPattern()); } } @@ -2811,12 +2813,9 @@ void PrintAST::printOneParameter(const ParamDecl *param, Printer << " = "; switch (param->getDefaultArgumentKind()) { - case DefaultArgumentKind::File: - case DefaultArgumentKind::Line: - case DefaultArgumentKind::Column: - case DefaultArgumentKind::Function: - case DefaultArgumentKind::DSOHandle: - case DefaultArgumentKind::NilLiteral: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: +#include "swift/AST/MagicIdentifierKinds.def" Printer.printKeyword(defaultArgStr, Options); break; default: @@ -2859,6 +2858,9 @@ void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { printParameterList(BodyParams, parameterListTypes, AFD->argumentNameIsAPIByDefault()); + if (AFD->hasAsync()) + Printer << " " << "async"; + if (AFD->hasThrows()) { if (AFD->getAttrs().hasAttribute()) Printer << " " << tok::kw_rethrows; @@ -3601,7 +3603,7 @@ 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); + info.getClangTypeInfo().getValue().printType(cml, os); Printer << ", cType: " << QuotedString(os.str()); } @@ -4022,9 +4024,8 @@ 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()) + // [TODO: Clang-type-plumbing] Remove the second check. + if (printNameOnly || !info.getClangTypeInfo().hasValue()) break; printCType(Ctx, Printer, info); break; @@ -4089,9 +4090,8 @@ 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()) + // [TODO: Clang-type-plumbing] Remove the second check. + if (printNameOnly || !info.getClangTypeInfo().hasValue()) break; printCType(Ctx, Printer, info); break; @@ -4104,7 +4104,8 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::WitnessMethod: Printer << "witness_method: "; printTypeDeclName( - witnessMethodConformance.getRequirement()->getDeclaredType()); + witnessMethodConformance.getRequirement()->getDeclaredType() + ->castTo()); break; case SILFunctionType::Representation::Closure: Printer << "closure"; @@ -4168,7 +4169,10 @@ class TypePrinter : public TypeVisitor { // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); - if (T->throws()) + if (T->isAsync()) + Printer << " " << "async"; + + if (T->isThrowing()) Printer << " " << tok::kw_throws; Printer << " -> "; @@ -4208,7 +4212,10 @@ class TypePrinter : public TypeVisitor { visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/true); - if (T->throws()) + if (T->isAsync()) + Printer << " " << "async"; + + if (T->isThrowing()) Printer << " " << tok::kw_throws; Printer << " -> "; @@ -5041,7 +5048,7 @@ swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options, isa(decl) && cast(decl)->hasRawType()) continue; - Results.push_back(TypeLoc::withoutLoc(proto->getDeclaredType())); + Results.push_back(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType())); } } } diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 019cc3ccfde42..c43874234a938 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -54,6 +54,16 @@ Optional ASTScope::computeIsCascadingUse( return ASTScopeImpl::computeIsCascadingUse(history, initialIsCascadingUse); } +llvm::SmallVector ASTScope::lookupLabeledStmts( + SourceFile *sourceFile, SourceLoc loc) { + return ASTScopeImpl::lookupLabeledStmts(sourceFile, loc); +} + +std::pair ASTScope::lookupFallthroughSourceAndDest( + SourceFile *sourceFile, SourceLoc loc) { + return ASTScopeImpl::lookupFallthroughSourceAndDest(sourceFile, loc); +} + #if SWIFT_COMPILER_IS_MSVC #pragma warning(push) #pragma warning(disable : 4996) @@ -237,6 +247,7 @@ DEFINE_GET_CLASS_NAME(WhileStmtScope) DEFINE_GET_CLASS_NAME(GuardStmtScope) DEFINE_GET_CLASS_NAME(LookupParentDiversionScope) DEFINE_GET_CLASS_NAME(RepeatWhileScope) +DEFINE_GET_CLASS_NAME(DoStmtScope) DEFINE_GET_CLASS_NAME(DoCatchStmtScope) DEFINE_GET_CLASS_NAME(SwitchStmtScope) DEFINE_GET_CLASS_NAME(ForEachStmtScope) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 8068de7c7fd88..e2b30ed302bb6 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -866,6 +866,7 @@ class NodeAdder VISIT_AND_CREATE(IfStmt, IfStmtScope) VISIT_AND_CREATE(WhileStmt, WhileStmtScope) VISIT_AND_CREATE(RepeatWhileStmt, RepeatWhileScope) + VISIT_AND_CREATE(DoStmt, DoStmtScope) VISIT_AND_CREATE(DoCatchStmt, DoCatchStmtScope) VISIT_AND_CREATE(SwitchStmt, SwitchStmtScope) VISIT_AND_CREATE(ForEachStmt, ForEachStmtScope) @@ -908,11 +909,6 @@ class NodeAdder ScopeCreator &scopeCreator) { return scopeCreator.ifUniqueConstructExpandAndInsert(p, e); } - NullablePtr visitDoStmt(DoStmt *ds, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - scopeCreator.addToScopeTreeAndReturnInsertionPoint(ds->getBody(), p); - return p; // Don't put subsequent decls inside the "do" - } NullablePtr visitTopLevelCodeDecl(TopLevelCodeDecl *d, ASTScopeImpl *p, ScopeCreator &scopeCreator) { @@ -1204,6 +1200,7 @@ NO_NEW_INSERTION_POINT(CaptureListScope) NO_NEW_INSERTION_POINT(CaseStmtScope) NO_NEW_INSERTION_POINT(ClosureBodyScope) NO_NEW_INSERTION_POINT(DefaultArgumentInitializerScope) +NO_NEW_INSERTION_POINT(DoStmtScope) NO_NEW_INSERTION_POINT(DoCatchStmtScope) NO_NEW_INSERTION_POINT(ForEachPatternScope) NO_NEW_INSERTION_POINT(ForEachStmtScope) @@ -1470,6 +1467,11 @@ void RepeatWhileScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.addToScopeTree(stmt->getCond(), this); } +void DoStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( + ScopeCreator &scopeCreator) { + scopeCreator.addToScopeTree(stmt->getBody(), this); +} + void DoCatchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { scopeCreator.addToScopeTree(stmt->getBody(), this); @@ -1599,9 +1601,13 @@ ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope( if (scope->shouldHaveABody() && !scope->doesDeclHaveABody()) return ip; + auto *context = scope->getGenericContext(); + auto *genericParams = (isa(context) + ? context->getParsedGenericParams() + : context->getGenericParams()); auto *deepestScope = scopeCreator.addNestedGenericParamScopesToTree( - scope->getDecl(), scope->getGenericContext()->getGenericParams(), scope); - if (scope->getGenericContext()->getTrailingWhereClause()) + scope->getDecl(), genericParams, scope); + if (context->getTrailingWhereClause()) scope->createTrailingWhereClauseScope(deepestScope, scopeCreator); scope->createBodyScope(deepestScope, scopeCreator); return ip; @@ -1777,7 +1783,6 @@ void ScopeCreator::forEachClosureIn( 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; } }; diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index b8d030295aaa3..13ab5b86a3177 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -292,7 +292,10 @@ NullablePtr GenericTypeScope::genericParams() const { // Sigh... These must be here so that from body, we search generics before // members. But they also must be on the Decl scope for lookups starting from // generic parameters, where clauses, etc. - return getGenericContext()->getGenericParams(); + auto *context = getGenericContext(); + if (isa(context)) + return context->getParsedGenericParams(); + return context->getGenericParams(); } NullablePtr ExtensionScope::genericParams() const { return decl->getGenericParams(); @@ -367,6 +370,22 @@ bool GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf( }); } +bool GenericTypeOrExtensionWherePortion::lookupMembersOf( + const GenericTypeOrExtensionScope *scope, + ArrayRef history, + ASTScopeImpl::DeclConsumer consumer) const { + if (!scope->areMembersVisibleFromWhereClause()) + return false; + + return GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf( + scope, history, consumer); +} + +bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const { + auto *decl = getDecl(); + return isa(decl) || isa(decl); +} + #pragma mark looking in locals or members - locals bool GenericParamScope::lookupLocalsOrMembers(ArrayRef, @@ -833,3 +852,84 @@ bool isLocWithinAnInactiveClause(const SourceLoc loc, SourceFile *SF) { SF->walk(tester); return tester.wasFoundWithinInactiveClause; } + +#pragma mark isLabeledStmtLookupTerminator implementations +bool ASTScopeImpl::isLabeledStmtLookupTerminator() const { + return true; +} + +bool LookupParentDiversionScope::isLabeledStmtLookupTerminator() const { + return false; +} + +bool ConditionalClauseScope::isLabeledStmtLookupTerminator() const { + return false; +} + +bool ConditionalClausePatternUseScope::isLabeledStmtLookupTerminator() const { + return false; +} + +bool AbstractStmtScope::isLabeledStmtLookupTerminator() const { + return false; +} + +bool ForEachPatternScope::isLabeledStmtLookupTerminator() const { + return false; +} + +llvm::SmallVector +ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) { + // Find the innermost scope from which to start our search. + auto *const fileScope = sourceFile->getScope().impl; + const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); + ASTScopeAssert(innermost->getWasExpanded(), + "If looking in a scope, it must have been expanded."); + + llvm::SmallVector labeledStmts; + for (auto scope = innermost; scope && !scope->isLabeledStmtLookupTerminator(); + scope = scope->getParent().getPtrOrNull()) { + // If we have a labeled statement, record it. + auto stmt = scope->getStmtIfAny(); + if (!stmt) continue; + + auto labeledStmt = dyn_cast(stmt.get()); + if (!labeledStmt) continue; + + // Skip guard statements; they aren't actually targets for break or + // continue. + if (isa(labeledStmt)) continue; + + labeledStmts.push_back(labeledStmt); + } + + return labeledStmts; +} + +std::pair ASTScopeImpl::lookupFallthroughSourceAndDest( + SourceFile *sourceFile, SourceLoc loc) { + // Find the innermost scope from which to start our search. + auto *const fileScope = sourceFile->getScope().impl; + const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); + ASTScopeAssert(innermost->getWasExpanded(), + "If looking in a scope, it must have been expanded."); + + // Look for the enclosing case statement of a 'switch'. + for (auto scope = innermost; scope && !scope->isLabeledStmtLookupTerminator(); + scope = scope->getParent().getPtrOrNull()) { + // If we have a case statement, record it. + auto stmt = scope->getStmtIfAny(); + if (!stmt) continue; + + // If we've found the first case statement of a switch, record it as the + // fallthrough source. do-catch statements don't support fallthrough. + if (auto caseStmt = dyn_cast(stmt.get())) { + if (caseStmt->getParentKind() == CaseParentKind::Switch) + return { caseStmt, caseStmt->findNextCaseStmt() }; + + continue; + } + } + + return { nullptr, nullptr }; +} diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index e155431f82dd6..aebc89a1c267a 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1040,7 +1040,8 @@ class Verifier : public ASTWalker { case StmtConditionElement::CK_Boolean: { auto *E = elt.getBoolean(); if (shouldVerifyChecked(E)) - checkSameType(E->getType(), Ctx.getBoolDecl()->getDeclaredType(), + checkSameType(E->getType(), + Ctx.getBoolDecl()->getDeclaredInterfaceType(), "condition type"); break; } @@ -1633,7 +1634,8 @@ class Verifier : public ASTWalker { abort(); } - checkSameType(E->getType(), anyHashableDecl->getDeclaredType(), + checkSameType(E->getType(), + anyHashableDecl->getDeclaredInterfaceType(), "AnyHashableErasureExpr and the standard AnyHashable type"); if (E->getConformance().getRequirement() != hashableDecl) { @@ -1799,7 +1801,7 @@ class Verifier : public ASTWalker { E->dump(Out); Out << "\n"; abort(); - } else if (E->throws() && !FT->throws()) { + } else if (E->throws() && !FT->isThrowing()) { Out << "apply expression is marked as throwing, but function operand" "does not have a throwing function type\n"; E->dump(Out); @@ -3022,7 +3024,7 @@ class Verifier : public ASTWalker { if (AFD->hasImplicitSelfDecl()) fnTy = fnTy->getResult()->castTo(); - if (AFD->hasThrows() != fnTy->getExtInfo().throws()) { + if (AFD->hasThrows() != fnTy->getExtInfo().isThrowing()) { Out << "function 'throws' flag does not match function type\n"; AFD->dump(Out); abort(); @@ -3355,7 +3357,7 @@ class Verifier : public ASTWalker { Type checkExceptionTypeExists(const char *where) { auto exn = Ctx.getErrorDecl(); - if (exn) return exn->getDeclaredType(); + if (exn) return exn->getDeclaredInterfaceType(); Out << "exception type does not exist in " << where << "\n"; abort(); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index e121f3528db75..3641f7c807627 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -129,7 +129,7 @@ class Traversal : public ASTVisitorgetGenericParams()) { + if (auto *params = GC->getParsedGenericParams()) { visitGenericParamList(params); } return true; @@ -142,11 +142,6 @@ class Traversal : public ASTVisitorgetRequirements()) if (doIt(Req)) return true; - } else if (!isa(GC)) { - if (const auto GP = GC->getGenericParams()) - for (auto Req: GP->getTrailingRequirements()) - if (doIt(Req)) - return true; } return false; } @@ -160,8 +155,9 @@ class Traversal : public ASTVisitorgetInherited()) { - if (doIt(Inherit)) - return true; + if (auto *const TyR = Inherit.getTypeRepr()) + if (doIt(TyR)) + return true; } if (visitTrailingRequirements(ED)) return true; @@ -263,9 +259,10 @@ class Traversal : public ASTVisitorgetInherited()) { - if (doIt(Inherit)) - return true; + for (const auto &Inherit: TPD->getInherited()) { + if (auto *const TyR = Inherit.getTypeRepr()) + if (doIt(TyR)) + return true; } if (const auto ATD = dyn_cast(TPD)) { @@ -287,9 +284,10 @@ class Traversal : public ASTVisitorgetInherited()) { - if (doIt(Inherit)) - return true; + for (const auto &Inherit : NTD->getInherited()) { + if (auto *const TyR = Inherit.getTypeRepr()) + if (doIt(Inherit.getTypeRepr())) + return true; } // Visit requirements @@ -360,8 +358,9 @@ class Traversal : public ASTVisitorgetIndices()); - if (doIt(SD->getElementTypeLoc())) - return true; + if (auto *const TyR = SD->getElementTypeLoc().getTypeRepr()) + if (doIt(TyR)) + return true; // Visit trailing requirements if (WalkGenerics && visitTrailingRequirements(SD)) @@ -393,10 +392,12 @@ class Traversal : public ASTVisitorgetParameters()); - if (auto *FD = dyn_cast(AFD)) + if (auto *FD = dyn_cast(AFD)) { if (!isa(FD)) - if (doIt(FD->getBodyResultTypeLoc())) - return true; + if (auto *const TyR = FD->getBodyResultTypeLoc().getTypeRepr()) + if (doIt(TyR)) + return true; + } // Visit trailing requirements if (WalkGenerics && visitTrailingRequirements(AFD)) @@ -446,7 +447,7 @@ class Traversal : public ASTVisitorgetNonTrailingRequirements()) { + for (auto Req : GPL->getRequirements()) { if (doIt(Req)) return true; } @@ -803,8 +804,16 @@ class Traversal : public ASTVisitorgetCaptureList()) { - if (doIt(c.Var) || doIt(c.Init)) + if (Walker.shouldWalkCaptureInitializerExpressions()) { + for (auto entryIdx : range(c.Init->getNumPatternEntries())) { + if (auto newInit = doIt(c.Init->getInit(entryIdx))) + c.Init->setInit(entryIdx, newInit); + else + return nullptr; + } + } else if (doIt(c.Var) || doIt(c.Init)) { return nullptr; + } } ClosureExpr *body = expr->getClosureBody(); @@ -825,7 +834,7 @@ class Traversal : public ASTVisitorwasSeparatelyTypeChecked() && + if (expr->isSeparatelyTypeChecked() && !Walker.shouldWalkIntoSeparatelyCheckedClosure(expr)) return expr; @@ -951,7 +960,11 @@ class Traversal : public ASTVisitorsetSubExpr(Sub); } - + + if (auto *typerepr = E->getCaseTypeRepr()) + if (doIt(typerepr)) + return nullptr; + return E; } @@ -1120,6 +1133,7 @@ class Traversal : public ASTVisitorgetMutableCaseLabelItems()) { if (auto *newPattern = doIt(CLI.getPattern())) - CLI.setPattern(newPattern); + CLI.setPattern(newPattern, /*resolved=*/CLI.isPatternResolved()); else return nullptr; if (CLI.getGuardExpr()) { @@ -1738,7 +1737,7 @@ Pattern *Traversal::visitExprPattern(ExprPattern *P) { return nullptr; } -Pattern *Traversal::visitVarPattern(VarPattern *P) { +Pattern *Traversal::visitBindingPattern(BindingPattern *P) { if (Pattern *newSub = doIt(P->getSubPattern())) { P->setSubPattern(newSub); return P; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index ac9989bc9cea0..69719bfb62ce6 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1679,8 +1679,9 @@ DifferentiableAttr::create(AbstractFunctionDecl *original, bool implicit, IndexSubset *parameterIndices, GenericSignature derivativeGenSig) { auto &ctx = original->getASTContext(); - void *mem = ctx.Allocate(sizeof(DifferentiableAttr), - alignof(DifferentiableAttr)); + + size_t size = totalSizeToAlloc(0); + void *mem = ctx.Allocate(size, alignof(DifferentiableAttr)); return new (mem) DifferentiableAttr(original, implicit, atLoc, baseRange, linear, parameterIndices, derivativeGenSig); } diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index da55b1f0cc01c..492e38a2cb861 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -282,7 +282,7 @@ GenericSignature autodiff::getConstrainedDerivativeGenericSignature( // Require differentiability parameters to conform to `Differentiable`. auto paramType = originalFnTy->getParameters()[paramIdx].getInterfaceType(); Requirement req(RequirementKind::Conformance, paramType, - diffableProto->getDeclaredType()); + diffableProto->getDeclaredInterfaceType()); requirements.push_back(req); if (isTranspose) { // Require linearity parameters to additionally satisfy @@ -372,6 +372,23 @@ bool autodiff::getBuiltinDifferentiableOrLinearFunctionConfig( return operationName.empty(); } +GenericSignature autodiff::getDifferentiabilityWitnessGenericSignature( + GenericSignature origGenSig, GenericSignature derivativeGenSig) { + // If there is no derivative generic signature, return the original generic + // signature. + if (!derivativeGenSig) + return origGenSig; + // If derivative generic signature has all concrete generic parameters and is + // equal to the original generic signature, return `nullptr`. + auto derivativeCanGenSig = derivativeGenSig.getCanonicalSignature(); + auto origCanGenSig = origGenSig.getCanonicalSignature(); + if (origCanGenSig == derivativeCanGenSig && + derivativeCanGenSig->areAllParamsConcrete()) + return GenericSignature(); + // Otherwise, return the derivative generic signature. + return derivativeGenSig; +} + Type TangentSpace::getType() const { switch (kind) { case Kind::TangentVector: diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index a1ebba5ca9003..a28946dc49bc4 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -318,6 +318,11 @@ ASTContext::getCompareProtocolConformanceDescriptorsAvailability() { return getSwiftFutureAvailability(); } +AvailabilityContext +ASTContext::getIntermodulePrespecializedGenericMetadataAvailability() { + return getSwiftFutureAvailability(); +} + AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 8c3bf3e7ec5d2..dde68fa9c1ea8 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/AST/Types.h" #include "swift/Strings.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -171,6 +172,7 @@ getBuiltinFunction(Identifier Id, ArrayRef argTypes, Type ResType) { StaticSpellingKind::None, /*FuncLoc=*/SourceLoc(), Name, /*NameLoc=*/SourceLoc(), + /*Async-*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, paramList, @@ -216,6 +218,7 @@ getBuiltinGenericFunction(Identifier Id, StaticSpellingKind::None, /*FuncLoc=*/SourceLoc(), Name, /*NameLoc=*/SourceLoc(), + /*Async-*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/ Rethrows, /*ThrowsLoc=*/SourceLoc(), GenericParams, paramList, @@ -493,7 +496,7 @@ namespace { void addConformanceRequirement(const G &generator, ProtocolDecl *proto) { Requirement req(RequirementKind::Conformance, generator.build(*this), - proto->getDeclaredType()); + proto->getDeclaredInterfaceType()); addedRequirements.push_back(req); } @@ -950,6 +953,15 @@ static ValueDecl *getUnsafeGuaranteedEnd(ASTContext &C, Identifier Id) { return getBuiltinFunction(Id, { Int8Ty }, TupleType::getEmpty(C)); } +static ValueDecl *getIntInstrprofIncrement(ASTContext &C, Identifier Id) { + // (Builtin.RawPointer, Builtin.Int64, Builtin.Int32, Builtin.Int32) -> () + Type Int64Ty = BuiltinIntegerType::get(64, C); + Type Int32Ty = BuiltinIntegerType::get(32, C); + return getBuiltinFunction(Id, + {C.TheRawPointerType, Int64Ty, Int32Ty, Int32Ty}, + TupleType::getEmpty(C)); +} + static ValueDecl *getTypePtrAuthDiscriminator(ASTContext &C, Identifier Id) { // (T.Type) -> Int64 BuiltinFunctionBuilder builder(C); @@ -1024,22 +1036,23 @@ static ValueDecl *getAutoDiffApplyDerivativeFunction( fnParamGens.push_back(T); } // Generator for the first argument, i.e. the `@differentiable` function. - BuiltinFunctionBuilder::LambdaGenerator firstArgGen { - // Generator for the function type at the argument position, i.e. the - // function being differentiated. - [=, &fnParamGens](BuiltinFunctionBuilder &builder) -> Type { - FunctionType::ExtInfo ext; - auto extInfo = FunctionType::ExtInfo() - .withDifferentiabilityKind(DifferentiabilityKind::Normal) - .withNoEscape().withThrows(throws); - SmallVector params; - for (auto ¶mGen : fnParamGens) - params.push_back(FunctionType::Param(paramGen.build(builder))); - auto innerFunction = FunctionType::get(params, - fnResultGen.build(builder)); - return innerFunction->withExtInfo(extInfo); - } - }; + BuiltinFunctionBuilder::LambdaGenerator firstArgGen{ + // Generator for the function type at the argument position, i.e. the + // function being differentiated. + [=, &fnParamGens](BuiltinFunctionBuilder &builder) -> Type { + auto extInfo = + FunctionType::ExtInfoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::Normal) + .withNoEscape() + .withThrows(throws) + .build(); + SmallVector params; + for (auto ¶mGen : fnParamGens) + params.push_back(FunctionType::Param(paramGen.build(builder))); + auto innerFunction = + FunctionType::get(params, fnResultGen.build(builder)); + return innerFunction->withExtInfo(extInfo); + }}; // Eagerly build the type of the first arg, then use that to compute the type // of the result. auto *diffFnType = @@ -1094,9 +1107,12 @@ static ValueDecl *getAutoDiffApplyTransposeFunction( // function being differentiated. [=, &linearFnParamGens](BuiltinFunctionBuilder &builder) -> Type { FunctionType::ExtInfo ext; - auto extInfo = FunctionType::ExtInfo() - .withDifferentiabilityKind(DifferentiabilityKind::Linear) - .withNoEscape().withThrows(throws); + auto extInfo = + FunctionType::ExtInfoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::Linear) + .withNoEscape() + .withThrows(throws) + .build(); SmallVector params; for (auto ¶mGen : linearFnParamGens) params.push_back(FunctionType::Param(paramGen.build(builder))); @@ -1152,8 +1168,9 @@ static ValueDecl *getDifferentiableFunctionConstructor( for (auto ¶mGen : fnArgGens) params.push_back(FunctionType::Param(paramGen.build(builder))); return FunctionType::get(params, origResultGen.build(builder)) - ->withExtInfo( - FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, throws)); + ->withExtInfo(FunctionType::ExtInfoBuilder( + FunctionTypeRepresentation::Swift, throws) + .build()); } }; @@ -1177,8 +1194,9 @@ static ValueDecl *getDifferentiableFunctionConstructor( {TupleTypeElt(origResultType, Context.Id_value), TupleTypeElt(differentialType, Context.Id_differential)}, Context); return FunctionType::get(params, jvpResultType) - ->withExtInfo( - FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, throws)); + ->withExtInfo(FunctionType::ExtInfoBuilder( + FunctionTypeRepresentation::Swift, throws) + .build()); } }; @@ -1205,8 +1223,9 @@ static ValueDecl *getDifferentiableFunctionConstructor( {TupleTypeElt(origResultType, Context.Id_value), TupleTypeElt(pullbackType, Context.Id_pullback)}, Context); return FunctionType::get(params, vjpResultType) - ->withExtInfo( - FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, throws)); + ->withExtInfo(FunctionType::ExtInfoBuilder( + FunctionTypeRepresentation::Swift, throws) + .build()); } }; @@ -1215,7 +1234,9 @@ static ValueDecl *getDifferentiableFunctionConstructor( auto origFnType = origFnGen.build(builder)->castTo(); return origFnType->withExtInfo( origFnType->getExtInfo() - .withDifferentiabilityKind(DifferentiabilityKind::Normal)); + .intoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::Normal) + .build()); } }; @@ -1254,8 +1275,9 @@ static ValueDecl *getLinearFunctionConstructor( for (auto ¶mGen : fnArgGens) params.push_back(FunctionType::Param(paramGen.build(builder))); return FunctionType::get(params, origResultGen.build(builder)) - ->withExtInfo( - FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, throws)); + ->withExtInfo(FunctionType::ExtInfoBuilder( + FunctionTypeRepresentation::Swift, throws) + .build()); } }; @@ -1278,7 +1300,9 @@ static ValueDecl *getLinearFunctionConstructor( auto origFnType = origFnGen.build(builder)->castTo(); return origFnType->withExtInfo( origFnType->getExtInfo() - .withDifferentiabilityKind(DifferentiabilityKind::Linear)); + .intoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::Linear) + .build()); } }; @@ -1528,8 +1552,10 @@ static ValueDecl *getOnceOperation(ASTContext &Context, auto HandleTy = Context.TheRawPointerType; auto VoidTy = Context.TheEmptyTupleType; - auto Thin = FunctionType::ExtInfo(FunctionTypeRepresentation::CFunctionPointer, - /*throws*/ false); + auto Thin = + FunctionType::ExtInfoBuilder(FunctionTypeRepresentation::CFunctionPointer, + /*throws*/ false) + .build(); if (withContext) { auto ContextTy = Context.TheRawPointerType; auto ContextArg = FunctionType::Param(ContextTy); @@ -2458,6 +2484,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { {}, TupleType::getEmpty(Context)); + case BuiltinValueKind::IntInstrprofIncrement: + return getIntInstrprofIncrement(Context, Id); + case BuiltinValueKind::TypePtrAuthDiscriminator: return getTypePtrAuthDiscriminator(Context, Id); diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 1cac247b15134..71a1cfa05c1ea 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -5,7 +5,7 @@ else() set(SWIFTAST_LLVM_LINK_COMPONENTS bitreader bitwriter coroutines coverage irreader debuginfoDWARF profiledata instrumentation object objcarcopts mc mcparser - bitreader bitwriter lto ipo option core support ${LLVM_TARGETS_TO_BUILD} + bitreader bitwriter lto ipo option core support remarks ${LLVM_TARGETS_TO_BUILD} ) endif() @@ -44,6 +44,7 @@ add_swift_host_library(swiftAST STATIC DocComment.cpp Evaluator.cpp Expr.cpp + ExtInfo.cpp FineGrainedDependencies.cpp FineGrainedDependencyFormat.cpp FrontendSourceFileDepGraphFactory.cpp diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index a0c7e71393b7e..d4ccea512f069 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -572,7 +572,7 @@ clang::QualType ClangTypeConverter::visitEnumType(EnumType *type) { clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { // We must've already computed it before if applicable. - return clang::QualType(type->getClangFunctionType(), 0); + return clang::QualType(type->getClangTypeInfo().getType(), 0); } clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 414d3d8803984..45a249601a7ac 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -464,8 +464,8 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc, } void ConformanceLookupTable::addInheritedProtocols( - llvm::PointerUnion decl, - ConformanceSource source) { + llvm::PointerUnion decl, + ConformanceSource source) { // Find all of the protocols in the inheritance list. bool anyObject = false; for (const auto &found : diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index c6843b32dabe1..28123413b498f 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -327,8 +327,8 @@ class ConformanceLookupTable { /// Add the protocols from the given list. void addInheritedProtocols( - llvm::PointerUnion decl, - ConformanceSource source); + llvm::PointerUnion decl, + ConformanceSource source); /// Expand the implied conformances for the given DeclContext. void expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e377aa4789b49..0b0816acd18b0 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -723,16 +723,13 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const { 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("_")) { + // If it's a function or subscript with a parameter with leading + // underscore, it's a private function or subscript. + if (isa(D) || isa(D)) { + const auto VD = cast(D); + if (getParameterList(const_cast(VD)) + ->hasInternalParameter("_")) { return true; } } @@ -891,8 +888,7 @@ GenericParamList::GenericParamList(SourceLoc LAngleLoc, SourceLoc RAngleLoc) : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), WhereLoc(WhereLoc), Requirements(Requirements), - OuterParameters(nullptr), - FirstTrailingWhereArg(Requirements.size()) + OuterParameters(nullptr) { std::uninitialized_copy(Params.begin(), Params.end(), getTrailingObjects()); @@ -931,39 +927,14 @@ GenericParamList::clone(DeclContext *dc) const { SmallVector params; for (auto param : getParams()) { auto *newParam = new (ctx) GenericTypeParamDecl( - dc, param->getName(), param->getNameLoc(), + dc, param->getName(), SourceLoc(), GenericTypeParamDecl::InvalidDepth, param->getIndex()); + newParam->setImplicit(true); params.push_back(newParam); } - return GenericParamList::create(ctx, - getLAngleLoc(), - params, - getWhereLoc(), - /*requirements=*/{}, - getRAngleLoc()); -} - -void GenericParamList::addTrailingWhereClause( - ASTContext &ctx, - SourceLoc trailingWhereLoc, - ArrayRef trailingRequirements) { - assert(TrailingWhereLoc.isInvalid() && - "Already have a trailing where clause?"); - TrailingWhereLoc = trailingWhereLoc; - FirstTrailingWhereArg = Requirements.size(); - - // Create a unified set of requirements. - auto newRequirements = ctx.AllocateUninitialized( - Requirements.size() + trailingRequirements.size()); - std::memcpy(newRequirements.data(), Requirements.data(), - Requirements.size() * sizeof(RequirementRepr)); - std::memcpy(newRequirements.data() + Requirements.size(), - trailingRequirements.data(), - trailingRequirements.size() * sizeof(RequirementRepr)); - - Requirements = newRequirements; + return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); } void GenericParamList::setDepth(unsigned depth) { @@ -971,6 +942,11 @@ void GenericParamList::setDepth(unsigned depth) { param->setDepth(depth); } +void GenericParamList::setDeclContext(DeclContext *dc) { + for (auto param : *this) + param->setDeclContext(dc); +} + TrailingWhereClause::TrailingWhereClause( SourceLoc whereLoc, ArrayRef requirements) @@ -994,9 +970,8 @@ GenericContext::GenericContext(DeclContextKind Kind, DeclContext *Parent, GenericParamList *Params) : _GenericContext(), DeclContext(Kind, Parent) { if (Params) { - Parent->getASTContext().evaluator.cacheOutput( - GenericParamListRequest{const_cast(this)}, - std::move(Params)); + Params->setDeclContext(this); + GenericParamsAndBit.setPointerAndInt(Params, false); } } @@ -1022,6 +997,12 @@ GenericParamList *GenericContext::getGenericParams() const { const_cast(this)}, nullptr); } +GenericParamList *GenericContext::getParsedGenericParams() const { + if (GenericParamsAndBit.getInt()) + return nullptr; + return GenericParamsAndBit.getPointer(); +} + bool GenericContext::hasComputedGenericSignature() const { return GenericSigAndBit.getInt(); } @@ -1051,9 +1032,7 @@ void GenericContext::setGenericSignature(GenericSignature genericSig) { } SourceRange GenericContext::getGenericTrailingWhereClauseSourceRange() const { - if (isGeneric()) - return getGenericParams()->getTrailingWhereClauseSourceRange(); - else if (const auto *where = getTrailingWhereClause()) + if (const auto *where = getTrailingWhereClause()) return where->getSourceRange(); return SourceRange(); @@ -1298,14 +1277,28 @@ bool ExtensionDecl::isConstrainedExtension() const { bool ExtensionDecl::isEquivalentToExtendedContext() const { auto decl = getExtendedNominal(); bool extendDeclFromSameModule = false; - if (!decl->getAlternateModuleName().empty()) { - // if the extended type was defined in the same module with the extension, - // we should consider them as the same module to preserve ABI stability. - extendDeclFromSameModule = decl->getAlternateModuleName() == - getParentModule()->getNameStr(); + auto extensionAlterName = getAlternateModuleName(); + auto typeAlterName = decl->getAlternateModuleName(); + + if (!extensionAlterName.empty()) { + if (!typeAlterName.empty()) { + // Case I: type and extension are both moved from somewhere else + extendDeclFromSameModule = typeAlterName == extensionAlterName; + } else { + // Case II: extension alone was moved from somewhere else + extendDeclFromSameModule = extensionAlterName == + decl->getParentModule()->getNameStr(); + } } else { - extendDeclFromSameModule = decl->getParentModule() == getParentModule(); + if (!typeAlterName.empty()) { + // Case III: extended type alone was moved from somewhere else + extendDeclFromSameModule = typeAlterName == getParentModule()->getNameStr(); + } else { + // Case IV: neither of type and extension was moved from somewhere else + extendDeclFromSameModule = getParentModule() == decl->getParentModule(); + } } + return extendDeclFromSameModule && !isConstrainedExtension() && !getDeclaredInterfaceType()->isExistentialType(); @@ -1334,114 +1327,6 @@ Type ExtensionDecl::getExtendedType() const { return ErrorType::get(ctx); } -/// Clone the given generic parameters in the given list. We don't need any -/// of the requirements, because they will be inferred. -static GenericParamList *cloneGenericParams(ASTContext &ctx, - ExtensionDecl *ext, - GenericParamList *fromParams) { - // Clone generic parameters. - SmallVector toGenericParams; - for (auto fromGP : *fromParams) { - // Create the new generic parameter. - auto toGP = new (ctx) GenericTypeParamDecl(ext, fromGP->getName(), - SourceLoc(), - fromGP->getDepth(), - fromGP->getIndex()); - toGP->setImplicit(true); - - // Record new generic parameter. - toGenericParams.push_back(toGP); - } - - return GenericParamList::create(ctx, SourceLoc(), toGenericParams, - SourceLoc()); -} - -static GenericParamList * -createExtensionGenericParams(ASTContext &ctx, - ExtensionDecl *ext, - NominalTypeDecl *nominal) { - // Collect generic parameters from all outer contexts. - SmallVector allGenericParams; - nominal->forEachGenericContext([&](GenericParamList *gpList) { - allGenericParams.push_back( - cloneGenericParams(ctx, ext, gpList)); - }); - - GenericParamList *toParams = nullptr; - for (auto *gpList : llvm::reverse(allGenericParams)) { - gpList->setOuterParameters(toParams); - toParams = gpList; - } - - return toParams; -} - -GenericParamList * -GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) const { - if (auto *ext = dyn_cast(value)) { - // Create the generic parameter list for the extension by cloning the - // generic parameter lists of the nominal and any of its parent types. - auto &ctx = value->getASTContext(); - auto *nominal = ext->getExtendedNominal(); - if (!nominal) { - return nullptr; - } - auto *genericParams = createExtensionGenericParams(ctx, ext, nominal); - - // Protocol extensions need an inheritance clause due to how name lookup - // is implemented. - if (auto *proto = ext->getExtendedProtocolDecl()) { - auto protoType = proto->getDeclaredType(); - TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; - genericParams->getParams().front()->setInherited( - ctx.AllocateCopy(selfInherited)); - } - - // Set the depth of every generic parameter. - unsigned depth = nominal->getGenericContextDepth(); - for (auto *outerParams = genericParams; - outerParams != nullptr; - outerParams = outerParams->getOuterParameters()) - outerParams->setDepth(depth--); - - // If we have a trailing where clause, deal with it now. - // For now, trailing where clauses are only permitted on protocol extensions. - if (auto trailingWhereClause = ext->getTrailingWhereClause()) { - if (genericParams) { - // Merge the trailing where clause into the generic parameter list. - // FIXME: Long-term, we'd like clients to deal with the trailing where - // clause explicitly, but for now it's far more direct to represent - // the trailing where clause as part of the requirements. - genericParams->addTrailingWhereClause( - ext->getASTContext(), - trailingWhereClause->getWhereLoc(), - trailingWhereClause->getRequirements()); - } - - // If there's no generic parameter list, the where clause is diagnosed - // in typeCheckDecl(). - } - return genericParams; - } else if (auto *proto = dyn_cast(value)) { - // The generic parameter 'Self'. - auto &ctx = value->getASTContext(); - auto selfId = ctx.Id_Self; - auto selfDecl = new (ctx) GenericTypeParamDecl( - proto, selfId, SourceLoc(), /*depth=*/0, /*index=*/0); - auto protoType = proto->getDeclaredType(); - TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; - selfDecl->setInherited(ctx.AllocateCopy(selfInherited)); - selfDecl->setImplicit(); - - // The generic parameter list itself. - auto result = GenericParamList::create(ctx, SourceLoc(), selfDecl, - SourceLoc()); - return result; - } - return nullptr; -} - PatternBindingDecl::PatternBindingDecl(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc VarLoc, @@ -1532,7 +1417,7 @@ PatternBindingDecl *PatternBindingDecl::createDeserialized( return PBD; } -ParamDecl *PatternBindingInitializer::getImplicitSelfDecl() { +ParamDecl *PatternBindingInitializer::getImplicitSelfDecl() const { if (SelfParam) return SelfParam; @@ -1544,12 +1429,14 @@ ParamDecl *PatternBindingInitializer::getImplicitSelfDecl() { : ParamSpecifier::InOut); ASTContext &C = DC->getASTContext(); - SelfParam = new (C) ParamDecl(SourceLoc(), SourceLoc(), + auto *mutableThis = const_cast(this); + auto *LazySelfParam = new (C) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), singleVar->getLoc(), - C.Id_self, this); - SelfParam->setImplicit(); - SelfParam->setSpecifier(specifier); - SelfParam->setInterfaceType(DC->getSelfInterfaceType()); + C.Id_self, mutableThis); + LazySelfParam->setImplicit(); + LazySelfParam->setSpecifier(specifier); + LazySelfParam->setInterfaceType(DC->getSelfInterfaceType()); + mutableThis->SelfParam = LazySelfParam; } } @@ -2689,9 +2576,11 @@ mapSignatureExtInfo(AnyFunctionType::ExtInfo info, bool topLevelFunction) { if (topLevelFunction) return AnyFunctionType::ExtInfo(); - return AnyFunctionType::ExtInfo() + return AnyFunctionType::ExtInfoBuilder() .withRepresentation(info.getRepresentation()) - .withThrows(info.throws()); + .withAsync(info.isAsync()) + .withThrows(info.isThrowing()) + .build(); } /// Map a function's type to the type used for computing signatures, @@ -4119,7 +4008,7 @@ StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, StructLoc(StructLoc) { Bits.StructDecl.HasUnreferenceableStorage = false; - Bits.StructDecl.IsCxxNotTriviallyCopyable = false; + Bits.StructDecl.IsCxxNonTrivial = false; } bool NominalTypeDecl::hasMemberwiseInitializer() const { @@ -4591,10 +4480,8 @@ bool ClassDecl::walkSuperclasses( EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc, ArrayRef Elements, DeclContext *DC) { - void *buf = DC->getASTContext() - .Allocate(sizeof(EnumCaseDecl) + - sizeof(EnumElementDecl*) * Elements.size(), - alignof(EnumCaseDecl)); + size_t bytes = totalSizeToAlloc(Elements.size()); + void *buf = DC->getASTContext().Allocate(bytes, alignof(EnumCaseDecl)); return ::new (buf) EnumCaseDecl(CaseLoc, Elements, DC); } @@ -5601,7 +5488,7 @@ SourceRange VarDecl::getTypeSourceRangeForDiagnostics() const { if (!Pat || Pat->isImplicit()) return SourceRange(); - if (auto *VP = dyn_cast(Pat)) + if (auto *VP = dyn_cast(Pat)) Pat = VP->getSubPattern(); if (auto *TP = dyn_cast(Pat)) if (auto typeRepr = TP->getTypeRepr()) @@ -6067,21 +5954,14 @@ bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const { return allAttachedPropertyWrappersHaveWrappedValueInit(); } -bool VarDecl::isInnermostPropertyWrapperInitUsesEscapingAutoClosure() const { - auto customAttrs = getAttachedPropertyWrappers(); - if (customAttrs.empty()) - return false; - - unsigned innermostWrapperIndex = customAttrs.size() - 1; - auto typeInfo = getAttachedPropertyWrapperTypeInfo(innermostWrapperIndex); - return typeInfo.isWrappedValueInitUsingEscapingAutoClosure; -} - Type VarDecl::getPropertyWrapperInitValueInterfaceType() const { - Type valueInterfaceTy = getValueInterfaceType(); + auto wrapperInfo = getPropertyWrapperBackingPropertyInfo(); + if (!wrapperInfo || !wrapperInfo.wrappedValuePlaceholder) + return Type(); - if (isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) - return FunctionType::get({}, valueInterfaceTy); + Type valueInterfaceTy = wrapperInfo.wrappedValuePlaceholder->getType(); + if (valueInterfaceTy->hasArchetype()) + valueInterfaceTy = valueInterfaceTy->mapTypeOutOfContext(); return valueInterfaceTy; } @@ -6348,12 +6228,9 @@ bool ParamDecl::hasDefaultExpr() const { case DefaultArgumentKind::StoredProperty: return false; case DefaultArgumentKind::Normal: - case DefaultArgumentKind::File: - case DefaultArgumentKind::FilePath: - case DefaultArgumentKind::Line: - case DefaultArgumentKind::Column: - case DefaultArgumentKind::Function: - case DefaultArgumentKind::DSOHandle: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: +#include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: @@ -6371,12 +6248,9 @@ bool ParamDecl::hasCallerSideDefaultExpr() const { case DefaultArgumentKind::StoredProperty: case DefaultArgumentKind::Normal: return false; - case DefaultArgumentKind::File: - case DefaultArgumentKind::FilePath: - case DefaultArgumentKind::Line: - case DefaultArgumentKind::Column: - case DefaultArgumentKind::Function: - case DefaultArgumentKind::DSOHandle: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: +#include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: @@ -6613,12 +6487,9 @@ ParamDecl::getDefaultValueStringRepresentation( scratch); } case DefaultArgumentKind::Inherited: return "super"; - case DefaultArgumentKind::File: return "#file"; - case DefaultArgumentKind::FilePath: return "#filePath"; - case DefaultArgumentKind::Line: return "#line"; - case DefaultArgumentKind::Column: return "#column"; - case DefaultArgumentKind::Function: return "#function"; - case DefaultArgumentKind::DSOHandle: return "#dsohandle"; +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: return STRING; +#include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::NilLiteral: return "nil"; case DefaultArgumentKind::EmptyArray: return "[]"; case DefaultArgumentKind::EmptyDictionary: return "[:]"; @@ -7222,6 +7093,7 @@ FuncDecl *FuncDecl::createImpl(ASTContext &Context, StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, DeclContext *Parent, @@ -7234,7 +7106,7 @@ FuncDecl *FuncDecl::createImpl(ASTContext &Context, !ClangN.isNull()); auto D = ::new (DeclPtr) FuncDecl(DeclKind::Func, StaticLoc, StaticSpelling, FuncLoc, - Name, NameLoc, Throws, ThrowsLoc, + Name, NameLoc, Async, AsyncLoc, Throws, ThrowsLoc, HasImplicitSelfDecl, GenericParams, Parent); if (ClangN) D->setClangNode(ClangN); @@ -7249,11 +7121,12 @@ FuncDecl *FuncDecl::createDeserialized(ASTContext &Context, StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, DeclContext *Parent) { return createImpl(Context, StaticLoc, StaticSpelling, FuncLoc, - Name, NameLoc, Throws, ThrowsLoc, + Name, NameLoc, Async, AsyncLoc, Throws, ThrowsLoc, GenericParams, Parent, ClangNode()); } @@ -7262,6 +7135,7 @@ FuncDecl *FuncDecl::create(ASTContext &Context, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, + bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, GenericParamList *GenericParams, ParameterList *BodyParams, @@ -7269,7 +7143,7 @@ FuncDecl *FuncDecl::create(ASTContext &Context, SourceLoc StaticLoc, ClangNode ClangN) { auto *FD = FuncDecl::createImpl( Context, StaticLoc, StaticSpelling, FuncLoc, - Name, NameLoc, Throws, ThrowsLoc, + Name, NameLoc, Async, AsyncLoc, Throws, ThrowsLoc, GenericParams, Parent, ClangN); FD->setParameters(BodyParams); FD->getBodyResultTypeLoc() = FnRetType; @@ -7456,7 +7330,8 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, GenericParamList *GenericParams, DeclContext *Parent) : AbstractFunctionDecl(DeclKind::Constructor, Parent, Name, ConstructorLoc, - Throws, ThrowsLoc, /*HasImplicitSelfDecl=*/true, + /*Async=*/false, SourceLoc(), Throws, ThrowsLoc, + /*HasImplicitSelfDecl=*/true, GenericParams), FailabilityLoc(FailabilityLoc), SelfDecl(nullptr) @@ -7487,8 +7362,8 @@ bool ConstructorDecl::isObjCZeroParameterWithLongSelector() const { DestructorDecl::DestructorDecl(SourceLoc DestructorLoc, DeclContext *Parent) : AbstractFunctionDecl(DeclKind::Destructor, Parent, DeclBaseName::createDestructor(), DestructorLoc, - /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), + /*Async=*/false, /*AsyncLoc=*/SourceLoc(), + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*HasImplicitSelfDecl=*/true, /*GenericParams=*/nullptr), SelfDecl(nullptr) { @@ -7533,6 +7408,9 @@ SourceRange FuncDecl::getSourceRange() const { if (hasThrows()) return { StartLoc, getThrowsLoc() }; + if (hasAsync()) + return { StartLoc, getAsyncLoc() }; + auto LastParamListEndLoc = getParameters()->getSourceRange().End; if (LastParamListEndLoc.isValid()) return { StartLoc, LastParamListEndLoc }; diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 00ab8b60e553e..5f7733df66c2b 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -301,13 +301,9 @@ bool DeclContext::isGenericContext() const { if (auto GC = decl->getAsGenericContext()) { if (GC->getGenericParams()) return true; - - // Extensions do not capture outer generic parameters. - if (isa(decl)) - break; } } - } while ((dc = dc->getParent())); + } while ((dc = dc->getParentForLookup())); return false; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 4ed744025e9c1..59cdfa8ab7cb7 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -289,6 +289,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const { ->getSubExpr()->getReferencedDecl(stopAtParenExpr); PASS_THROUGH_REFERENCE(DotSelf, getSubExpr); + PASS_THROUGH_REFERENCE(Await, getSubExpr); PASS_THROUGH_REFERENCE(Try, getSubExpr); PASS_THROUGH_REFERENCE(ForceTry, getSubExpr); PASS_THROUGH_REFERENCE(OptionalTry, getSubExpr); @@ -422,7 +423,6 @@ forEachImmediateChildExpr(llvm::function_ref callback) { } bool walkToDeclPre(Decl *D) override { return false; } bool walkToTypeReprPre(TypeRepr *T) override { return false; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } }; this->walk(ChildWalker(callback, this)); @@ -452,7 +452,6 @@ void Expr::forEachChildExpr(llvm::function_ref callback) { } bool walkToDeclPre(Decl *D) override { return false; } bool walkToTypeReprPre(TypeRepr *T) override { return false; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } }; this->walk(ChildWalker(callback)); @@ -623,6 +622,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const { case ExprKind::DynamicType: return true; + case ExprKind::Await: case ExprKind::Try: case ExprKind::ForceTry: case ExprKind::OptionalTry: @@ -1204,9 +1204,8 @@ InOutExpr::InOutExpr(SourceLoc operLoc, Expr *subExpr, Type baseType, SequenceExpr *SequenceExpr::create(ASTContext &ctx, ArrayRef elements) { assert(elements.size() & 1 && "even number of elements in sequence"); - void *Buffer = ctx.Allocate(sizeof(SequenceExpr) + - elements.size() * sizeof(Expr*), - alignof(SequenceExpr)); + size_t bytes = totalSizeToAlloc(elements.size()); + void *Buffer = ctx.Allocate(bytes, alignof(SequenceExpr)); return ::new(Buffer) SequenceExpr(elements); } @@ -1278,15 +1277,29 @@ SourceRange TupleExpr::getSourceRange() const { return { SourceLoc(), SourceLoc() }; } else { // Scan backwards for a valid source loc. + bool hasSingleTrailingClosure = hasTrailingClosure(); for (Expr *expr : llvm::reverse(getElements())) { // Default arguments are located at the start of their parent tuple, so // skip over them. if (isa(expr)) continue; - end = expr->getEndLoc(); - if (end.isValid()) { - break; + + SourceLoc newEnd = expr->getEndLoc(); + if (newEnd.isInvalid()) + continue; + + // There is a quirk with the backward scan logic for trailing + // closures that can cause arguments to be flipped. If there is a + // single trailing closure, only stop when the "end" point we hit comes + // after the close parenthesis (if there is one). + if (end.isInvalid() || + end.getOpaquePointerValue() < newEnd.getOpaquePointerValue()) { + end = newEnd; } + + if (!hasSingleTrailingClosure || RParenLoc.isInvalid() || + RParenLoc.getOpaquePointerValue() < end.getOpaquePointerValue()) + break; } } } else { @@ -1463,13 +1476,13 @@ static ValueDecl *getCalledValue(Expr *E) { PropertyWrapperValuePlaceholderExpr * PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range, - Type ty, Expr *wrappedValue) { + Type ty, Expr *wrappedValue, + bool isAutoClosure) { auto *placeholder = new (ctx) OpaqueValueExpr(range, ty, /*isPlaceholder=*/true); - return new (ctx) PropertyWrapperValuePlaceholderExpr(range, ty, - placeholder, - wrappedValue); + return new (ctx) PropertyWrapperValuePlaceholderExpr( + range, ty, placeholder, wrappedValue, isAutoClosure); } const ParamDecl *DefaultArgumentExpr::getParamDecl() const { @@ -1808,17 +1821,6 @@ ConditionalCheckedCastExpr::createImplicit(ASTContext &ctx, Expr *sub, return expr; } -ConditionalCheckedCastExpr * -ConditionalCheckedCastExpr::createImplicit(ASTContext &ctx, Expr *sub, - TypeRepr *tyRepr, Type castTy) { - auto *const expr = new (ctx) ConditionalCheckedCastExpr( - sub, SourceLoc(), SourceLoc(), new (ctx) TypeExpr(tyRepr)); - expr->setType(OptionalType::get(castTy)); - expr->setImplicit(); - expr->setCastType(castTy); - return expr; -} - IsExpr *IsExpr::create(ASTContext &ctx, SourceLoc isLoc, TypeRepr *tyRepr) { return new (ctx) IsExpr(nullptr, isLoc, new (ctx) TypeExpr(tyRepr)); } @@ -1926,8 +1928,15 @@ Type AbstractClosureExpr::getResultType( bool AbstractClosureExpr::isBodyThrowing() const { if (getType()->hasError()) return false; + + return getType()->castTo()->getExtInfo().isThrowing(); +} + +bool AbstractClosureExpr::isBodyAsync() const { + if (getType()->hasError()) + return false; - return getType()->castTo()->getExtInfo().throws(); + return getType()->castTo()->getExtInfo().isAsync(); } bool AbstractClosureExpr::hasSingleExpressionBody() const { @@ -1979,14 +1988,21 @@ bool ClosureExpr::hasEmptyBody() const { } bool ClosureExpr::capturesSelfEnablingImplictSelf() const { - if (auto *VD = getCapturedSelfDecl()) - return VD->isSelfParamCapture() && !VD->getType()->is(); + if (auto *VD = getCapturedSelfDecl()) { + if (!VD->isSelfParamCapture()) + return false; + + if (auto *attr = VD->getAttrs().getAttribute()) + return attr->get() != ReferenceOwnership::Weak; + + return true; + } return false; } void ClosureExpr::setExplicitResultType(Type ty) { assert(ty && !ty->hasTypeVariable()); - ExplicitResultTypeAndSeparatelyChecked.getPointer() + ExplicitResultTypeAndBodyState.getPointer() ->setType(MetatypeType::get(ty)); } @@ -2367,6 +2383,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances( case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::DictionaryKey: llvm_unreachable("no hashable conformances for this kind"); } } @@ -2475,3 +2492,4 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp new file mode 100644 index 0000000000000..a75a3ff1d9f5e --- /dev/null +++ b/lib/AST/ExtInfo.cpp @@ -0,0 +1,93 @@ +//===--- ExtInfo.cpp - Extended information for function types ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 ASTExtInfo, SILExtInfo and related classes. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/ExtInfo.h" + +#include "clang/AST/Type.h" + +namespace swift { + +// MARK: - ClangTypeInfo + +bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs) { + if (lhs.type == rhs.type) + return true; + if (lhs.type && rhs.type) + return lhs.type->getCanonicalTypeInternal() == + rhs.type->getCanonicalTypeInternal(); + return false; +} + +ClangTypeInfo ClangTypeInfo::getCanonical() const { + if (!type) + return ClangTypeInfo(); + return ClangTypeInfo(type->getCanonicalTypeInternal().getTypePtr()); +} + +void ClangTypeInfo::printType(ClangModuleLoader *cml, + llvm::raw_ostream &os) const { + cml->printClangType(type, os); +} + +void ClangTypeInfo::dump(llvm::raw_ostream &os) const { + if (type) { + type->dump(os); + } else { + os << ""; + } +} + +// MARK: - ASTExtInfoBuilder + +void ASTExtInfoBuilder::assertIsFunctionType(const clang::Type *type) { +#ifndef NDEBUG + if (!(type->isFunctionPointerType() || type->isBlockPointerType() || + type->isFunctionReferenceType())) { + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Expected a Clang function type wrapped in a pointer type or " + << "a block pointer type but found:\n"; + type->dump(os); + llvm_unreachable(os.str().data()); + } +#endif + return; +} + +void ASTExtInfoBuilder::checkInvariants() const { + // TODO: Add validation checks here while making sure things don't blow up. +} + +// MARK: - ASTExtInfo + +ASTExtInfo ASTExtInfoBuilder::build() const { + checkInvariants(); + return ASTExtInfo(*this); +} + +// MARK: - SILExtInfoBuilder + +void SILExtInfoBuilder::checkInvariants() const { + // TODO: Add validation checks here while making sure things don't blow up. +} + +SILExtInfo SILExtInfoBuilder::build() const { + checkInvariants(); + return SILExtInfo(*this); +} + +} // end namespace swift diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 7d05c54be064f..dc03dc5cc0c61 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -2065,18 +2065,9 @@ Type EquivalenceClass::getTypeInContext(GenericSignatureBuilder &builder, return ErrorType::get(anchor); // Map the parent type into this context. - Type parentType = parentEquivClass->getTypeInContext(builder, genericEnv); - - // If the parent is concrete, handle the - parentArchetype = parentType->getAs(); - if (!parentArchetype) { - // Resolve the member type. - Type memberType = - depMemTy->substBaseType(parentType, builder.getLookupConformanceFn()); - - return genericEnv->mapTypeIntoContext(memberType, - builder.getLookupConformanceFn()); - } + parentArchetype = + parentEquivClass->getTypeInContext(builder, genericEnv) + ->castTo(); // If we already have a nested type with this name, return it. assocType = depMemTy->getAssocType(); @@ -2380,20 +2371,9 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( return superclassSource; } -/// Realize a potential archetype for this type parameter. -PotentialArchetype *ResolvedType::realizePotentialArchetype( - GenericSignatureBuilder &builder) { - // Realize and cache the potential archetype. - return builder.realizePotentialArchetype(type); -} - Type ResolvedType::getDependentType(GenericSignatureBuilder &builder) const { - // Already-resolved potential archetype. - if (auto pa = type.dyn_cast()) - return pa->getDependentType(builder.getGenericParams()); - - Type result = type.get(); - return result->isTypeParameter() ? result : Type(); + return storage.get() + ->getDependentType(builder.getGenericParams()); } auto PotentialArchetype::getOrCreateEquivalenceClass( @@ -3550,20 +3530,6 @@ static Type resolveDependentMemberTypes( }); } -PotentialArchetype *GenericSignatureBuilder::realizePotentialArchetype( - UnresolvedType &type) { - if (auto pa = type.dyn_cast()) - return pa; - - auto pa = maybeResolveEquivalenceClass(type.get(), - ArchetypeResolutionKind::WellFormed, - /*wantExactPotentialArchetype=*/true) - .getPotentialArchetypeIfKnown(); - if (pa) type = pa; - - return pa; -} - static Type getStructuralType(TypeDecl *typeDecl, bool keepSugar) { if (auto typealias = dyn_cast(typeDecl)) { if (typealias->getUnderlyingTypeRepr() != nullptr) { @@ -3677,19 +3643,7 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass( if (!nestedPA) return ResolvedType::forUnresolved(baseEquivClass); - // If base resolved to the anchor, then the nested potential archetype - // we found is the resolved potential archetype. Return it directly, - // so it doesn't need to be resolved again. - if (basePA == resolvedBase.getPotentialArchetypeIfKnown()) - return ResolvedType(nestedPA); - - // Compute the resolved dependent type to return. - Type resolvedBaseType = resolvedBase.getDependentType(*this); - Type resolvedMemberType = - DependentMemberType::get(resolvedBaseType, assocType); - - return ResolvedType(resolvedMemberType, - nestedPA->getOrCreateEquivalenceClass(*this)); + return ResolvedType(nestedPA); } else { auto *concreteDecl = baseEquivClass->lookupNestedType(*this, depMemTy->getName()); @@ -3814,8 +3768,8 @@ void GenericSignatureBuilder::addGenericParameter(GenericTypeParamType *GenericP /// Visit all of the types that show up in the list of inherited /// types. static ConstraintResult visitInherited( - llvm::PointerUnion decl, - llvm::function_ref visitType) { + llvm::PointerUnion decl, + llvm::function_ref visitType) { // Local function that (recursively) adds inherited types. ConstraintResult result = ConstraintResult::Resolved; std::function visitInherited; @@ -3841,8 +3795,8 @@ static ConstraintResult visitInherited( }; // Visit all of the inherited types. - auto typeDecl = decl.dyn_cast(); - auto extDecl = decl.dyn_cast(); + auto typeDecl = decl.dyn_cast(); + auto extDecl = decl.dyn_cast(); ASTContext &ctx = typeDecl ? typeDecl->getASTContext() : extDecl->getASTContext(); auto &evaluator = ctx.evaluator; @@ -4561,8 +4515,7 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( if (equivClass2) { equivClass = equivClass2; } else { - auto pa1 = type1.realizePotentialArchetype(*this); - equivClass = pa1->getOrCreateEquivalenceClass(*this); + equivClass = type1.getEquivalenceClass(*this); } } @@ -4574,8 +4527,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( // Both sides are type parameters; equate them. // FIXME: Realizes potential archetypes far too early. - auto OrigT1 = type1.realizePotentialArchetype(*this); - auto OrigT2 = type2.realizePotentialArchetype(*this); + auto OrigT1 = type1.getPotentialArchetypeIfKnown(); + auto OrigT2 = type2.getPotentialArchetypeIfKnown(); // Operate on the representatives auto T1 = OrigT1->getRepresentative(); @@ -5115,7 +5068,7 @@ class GenericSignatureBuilder::InferRequirementsWalker : public TypeWalker { if (differentiableProtocol && fnTy->isDifferentiable()) { auto addConformanceConstraint = [&](Type type, ProtocolDecl *protocol) { Requirement req(RequirementKind::Conformance, type, - protocol->getDeclaredType()); + protocol->getDeclaredInterfaceType()); Builder.addRequirement(req, source, nullptr); }; auto addSameTypeConstraint = [&](Type firstType, @@ -6265,7 +6218,7 @@ static bool removalDisconnectsEquivalenceClass( // derived edges). if (fromComponentIndex == toComponentIndex) return false; - /// Describes the parents in the equivalance classes we're forming. + /// Describes the parents in the equivalence classes we're forming. SmallVector parents; for (unsigned i : range(equivClass->derivedSameTypeComponents.size())) { parents.push_back(i); @@ -7638,17 +7591,17 @@ InferredGenericSignatureRequest::evaluate( .visitRequirements(TypeResolutionStage::Structural, visitRequirement); } - } else { - // The declaration has a where clause, but no generic parameters of its own. - const auto ctx = paramSource.get(); - - assert(ctx->getTrailingWhereClause() && "No params or where clause"); + } - // Determine where and how to perform name lookup. - lookupDC = ctx; + if (auto *ctx = paramSource.dyn_cast()) { + // The declaration might have a trailing where clause. + if (auto *where = ctx->getTrailingWhereClause()) { + // Determine where and how to perform name lookup. + lookupDC = ctx; - WhereClauseOwner(ctx).visitRequirements( - TypeResolutionStage::Structural, visitRequirement); + WhereClauseOwner(lookupDC, where).visitRequirements( + TypeResolutionStage::Structural, visitRequirement); + } } /// Perform any remaining requirement inference. diff --git a/lib/AST/GenericSignatureBuilderImpl.h b/lib/AST/GenericSignatureBuilderImpl.h index 88467f5a28929..2f08c55b630a1 100644 --- a/lib/AST/GenericSignatureBuilderImpl.h +++ b/lib/AST/GenericSignatureBuilderImpl.h @@ -13,25 +13,20 @@ namespace swift { class GenericSignatureBuilder::ResolvedType { - llvm::PointerUnion type; - EquivalenceClass *equivClass; + llvm::PointerUnion storage; /// For a type that could not be resolved further unless the given /// equivalence class changes. - ResolvedType(EquivalenceClass *equivClass) - : type(), equivClass(equivClass) { } + ResolvedType(EquivalenceClass *equivClass) : storage(equivClass) { } + + /// A concrete resolved type. + ResolvedType(Type type) : storage(type) { + assert(!type->isTypeParameter()); + } public: /// A specific resolved potential archetype. - ResolvedType(PotentialArchetype *pa) - : type(pa), equivClass(pa->getEquivalenceClassIfPresent()) { } - - /// A resolved type within the given equivalence class. - ResolvedType(Type type, EquivalenceClass *equivClass) - : type(type), equivClass(equivClass) { - assert(type->isTypeParameter() == static_cast(equivClass) && - "type parameters must have equivalence classes"); - } + ResolvedType(PotentialArchetype *pa) : storage(pa) { } /// Return an unresolved result, which could be resolved when we /// learn more information about the given equivalence class. @@ -41,11 +36,13 @@ class GenericSignatureBuilder::ResolvedType { /// Return a result for a concrete type. static ResolvedType forConcrete(Type concreteType) { - return ResolvedType(concreteType, nullptr); + return ResolvedType(concreteType); } /// Determine whether this result was resolved. - explicit operator bool() const { return !type.isNull(); } + explicit operator bool() const { + return storage.is() || storage.is(); + } /// Retrieve the dependent type. Type getDependentType(GenericSignatureBuilder &builder) const; @@ -54,51 +51,38 @@ class GenericSignatureBuilder::ResolvedType { /// a concrete type. Type getAsConcreteType() const { assert(*this && "Doesn't contain any result"); - if (equivClass) return Type(); - return type.dyn_cast(); + return storage.dyn_cast(); } - /// Realize a potential archetype for this type parameter. - PotentialArchetype *realizePotentialArchetype( - GenericSignatureBuilder &builder); - /// Retrieve the potential archetype, if already known. PotentialArchetype *getPotentialArchetypeIfKnown() const { - return type.dyn_cast(); + return storage.dyn_cast(); } /// Retrieve the equivalence class into which a resolved type refers. EquivalenceClass *getEquivalenceClass( GenericSignatureBuilder &builder) const { - assert(*this && "Only for resolved types"); - if (equivClass) return equivClass; - - // Create the equivalence class now. - return type.get() + return storage.get() ->getOrCreateEquivalenceClass(builder); } /// Retrieve the equivalence class into which a resolved type refers. EquivalenceClass *getEquivalenceClassIfPresent() const { - assert(*this && "Only for resolved types"); - if (equivClass) return equivClass; - - // Create the equivalence class now. - return type.get()->getEquivalenceClassIfPresent(); + return storage.get() + ->getEquivalenceClassIfPresent(); } /// Retrieve the unresolved result. EquivalenceClass *getUnresolvedEquivClass() const { - assert(!*this); - return equivClass; + return storage.dyn_cast(); } /// Return an unresolved type. - /// - /// This loses equivalence-class information that could be useful, which - /// is unfortunate. UnresolvedType getUnresolvedType() const { - return type; + assert(!storage.is()); + if (storage.is()) + return storage.get(); + return storage.get(); } }; diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index ed9c77b3631f9..d6709a780d122 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -139,12 +139,9 @@ ImportCache::getImportSet(ASTContext &ctx, // getImportedModulesForLookup(). if (ImportSet *result = ImportSets.FindNodeOrInsertPos(ID, InsertPos)) return *result; - - void *mem = ctx.Allocate( - sizeof(ImportSet) + - sizeof(ModuleDecl::ImportedModule) * topLevelImports.size() + - sizeof(ModuleDecl::ImportedModule) * transitiveImports.size(), - alignof(ImportSet), AllocationArena::Permanent); + + size_t bytes = ImportSet::totalSizeToAlloc(topLevelImports.size() + transitiveImports.size()); + void *mem = ctx.Allocate(bytes, alignof(ImportSet), AllocationArena::Permanent); auto *result = new (mem) ImportSet(hasHeaderImportModule, topLevelImports, diff --git a/lib/AST/LocalizationFormat.cpp b/lib/AST/LocalizationFormat.cpp index f0af4a133288a..4c8ead4d9729a 100644 --- a/lib/AST/LocalizationFormat.cpp +++ b/lib/AST/LocalizationFormat.cpp @@ -1,5 +1,4 @@ -//===--- LocalizationFormat.cpp - YAML format for Diagnostic Messages ---*- -// C++ -*-===// +//===-- LocalizationFormat.cpp - Format for Diagnostic Messages -*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -18,11 +17,14 @@ #include "swift/AST/LocalizationFormat.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" +#include #include +#include #include namespace { @@ -46,6 +48,10 @@ template <> struct ScalarEnumerationTraits { #define DIAG(KIND, ID, Options, Text, Signature) \ io.enumCase(value, #ID, LocalDiagID::ID); #include "swift/AST/DiagnosticsAll.def" + // Ignore diagnostic IDs that are available in the YAML file and not + // available in the `.def` file. + if (io.matchEnumFallback()) + value = LocalDiagID::NumDiags; } }; @@ -64,15 +70,54 @@ template <> struct MappingTraits { namespace swift { namespace diag { -YAMLLocalizationProducer::YAMLLocalizationProducer(std::string locale, - std::string path) { - llvm::SmallString<128> DiagnosticsFilePath(path); - llvm::sys::path::append(DiagnosticsFilePath, locale); - llvm::sys::path::replace_extension(DiagnosticsFilePath, ".yaml"); - auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(DiagnosticsFilePath); - // Absence of localizations shouldn't crash the compiler. - if (!FileBufOrErr) - return; +void SerializedLocalizationWriter::insert(swift::DiagID id, + llvm::StringRef translation) { + generator.insert(static_cast(id), translation); +} + +bool SerializedLocalizationWriter::emit(llvm::StringRef filePath) { + assert(llvm::sys::path::extension(filePath) == ".db"); + std::error_code error; + llvm::raw_fd_ostream OS(filePath, error, llvm::sys::fs::F_None); + if (OS.has_error()) { + return true; + } + + offset_type offset; + { + llvm::support::endian::write(OS, 0, llvm::support::little); + offset = generator.Emit(OS); + } + OS.seek(0); + llvm::support::endian::write(OS, offset, llvm::support::little); + OS.close(); + + return OS.has_error(); +} + +SerializedLocalizationProducer::SerializedLocalizationProducer( + std::unique_ptr buffer) + : Buffer(std::move(buffer)) { + auto base = + reinterpret_cast(Buffer.get()->getBufferStart()); + auto tableOffset = endian::read(base, little); + SerializedTable.reset(SerializedLocalizationTable::Create( + base + tableOffset, base + sizeof(offset_type), base)); +} + +llvm::StringRef SerializedLocalizationProducer::getMessageOr( + swift::DiagID id, llvm::StringRef defaultMessage) const { + auto value = SerializedTable.get()->find(id); + llvm::StringRef diagnosticMessage((const char *)value.getDataPtr(), + value.getDataLen()); + if (diagnosticMessage.empty()) + return defaultMessage; + + return diagnosticMessage; +} + +YAMLLocalizationProducer::YAMLLocalizationProducer(llvm::StringRef filePath) { + auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(filePath); llvm::MemoryBuffer *document = FileBufOrErr->get(); diag::LocalizationInput yin(document->getBuffer()); yin >> diagnostics; @@ -89,6 +134,15 @@ YAMLLocalizationProducer::getMessageOr(swift::DiagID id, return diagnosticMessage; } +void YAMLLocalizationProducer::forEachAvailable( + llvm::function_ref callback) const { + for (uint32_t i = 0, n = diagnostics.size(); i != n; ++i) { + auto translation = diagnostics[i]; + if (!translation.empty()) + callback(static_cast(i), translation); + } +} + template typename std::enable_if::value, void>::type readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx) { @@ -101,12 +155,19 @@ readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx) { DiagnosticNode current; yamlize(io, current, true, Ctx); io.postflightElement(SaveInfo); - // YAML file isn't guaranteed to have diagnostics in order of their - // declaration in `.def` files, to accommodate that we need to leave - // holes in diagnostic array for diagnostics which haven't yet been - // localized and for the ones that have `DiagnosticNode::id` - // indicates their position. - Seq[static_cast(current.id)] = std::move(current.msg); + + // A diagnostic ID might be present in YAML and not in `.def` file, + // if that's the case ScalarEnumerationTraits will assign the diagnostic ID + // to `LocalDiagID::NumDiags`. Since the diagnostic ID isn't available + // in `.def` it shouldn't be stored in the diagnostics array. + if (current.id != LocalDiagID::NumDiags) { + // YAML file isn't guaranteed to have diagnostics in order of their + // declaration in `.def` files, to accommodate that we need to leave + // holes in diagnostic array for diagnostics which haven't yet been + // localized and for the ones that have `DiagnosticNode::id` + // indicates their position. + Seq[static_cast(current.id)] = std::move(current.msg); + } } } io.endSequence(); diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 874cdbdedd57c..6749fdc8b8802 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -493,9 +493,6 @@ ArrayRef ModuleDecl::getImplicitImports() const { {}); } -bool ModuleDecl::isClangModule() const { - return findUnderlyingClangModule() != nullptr; -} void ModuleDecl::addFile(FileUnit &newFile) { // If this is a LoadedFile, make sure it loaded without error. @@ -1459,9 +1456,10 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const llvm::SmallDenseSet visited; SmallVector stack; - ModuleDecl::ImportFilter filter = ModuleDecl::ImportFilterKind::Public; - filter |= ModuleDecl::ImportFilterKind::Private; - filter |= ModuleDecl::ImportFilterKind::SPIAccessControl; + ModuleDecl::ImportFilter filter = { + ModuleDecl::ImportFilterKind::Public, + ModuleDecl::ImportFilterKind::Private, + ModuleDecl::ImportFilterKind::SPIAccessControl}; auto *topLevel = getParentModule(); @@ -2053,7 +2051,15 @@ SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const { for (auto spi : attr->getSPIGroups()) spiGroups.insert(spi); - auto &ctx = decl->getASTContext(); + // Backing storage for a wrapped property gets the SPI groups from the + // original property. + if (auto varDecl = dyn_cast(decl)) + if (auto originalDecl = varDecl->getOriginalWrappedProperty()) { + auto originalSPIs = originalDecl->getSPIGroups(); + spiGroups.insert(originalSPIs.begin(), originalSPIs.end()); + } + + // If there is no local SPI information, look at the context. if (spiGroups.empty()) { // Then in the extended nominal type. @@ -2073,6 +2079,7 @@ SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const { } } + auto &ctx = decl->getASTContext(); return ctx.AllocateCopy(spiGroups.getArrayRef()); } @@ -2141,20 +2148,18 @@ getInfoForUsedFileNames(const ModuleDecl *module) { return result; } -static void -computeMagicFileString(const ModuleDecl *module, StringRef name, - SmallVectorImpl &result) { +static void computeFileID(const ModuleDecl *module, StringRef name, + SmallVectorImpl &result) { result.assign(module->getNameStr().begin(), module->getNameStr().end()); result.push_back('/'); result.append(name.begin(), name.end()); } static StringRef -resolveMagicNameConflicts(const ModuleDecl *module, StringRef fileString, - const llvm::StringMap &paths, - bool shouldDiagnose) { +resolveFileIDConflicts(const ModuleDecl *module, StringRef fileString, + const llvm::StringMap &paths, + bool shouldDiagnose) { assert(paths.size() > 1); - assert(module->getASTContext().LangOpts.EnableConcisePoundFile); /// The path we consider to be "correct"; we will emit fix-its changing the /// other paths to match this one. @@ -2191,13 +2196,20 @@ resolveMagicNameConflicts(const ModuleDecl *module, StringRef fileString, // Don't diagnose #sourceLocations that match the physical file. if (pathPair.second.physicalFileLoc.isValid()) { - assert(isWinner && "physical files should always win; duplicate name?"); + if (!isWinner) { + // The driver is responsible for diagnosing this, but naughty people who + // have directly invoked the frontend could make it happen here instead. + StringRef filename = llvm::sys::path::filename(winner); + diags.diagnose(SourceLoc(), diag::error_two_files_same_name, + filename, winner, pathPair.first()); + diags.diagnose(SourceLoc(), diag::note_explain_two_files_same_name); + } continue; } for (auto loc : pathPair.second.virtualFileLocs) { diags.diagnose(loc, - diag::pound_source_location_creates_pound_file_conflicts, + diag::source_location_creates_file_id_conflicts, fileString); // Offer a fix-it unless it would be tautological. @@ -2211,26 +2223,23 @@ resolveMagicNameConflicts(const ModuleDecl *module, StringRef fileString, } llvm::StringMap> -ModuleDecl::computeMagicFileStringMap(bool shouldDiagnose) const { +ModuleDecl::computeFileIDMap(bool shouldDiagnose) const { llvm::StringMap> result; SmallString<64> scratch; - if (!getASTContext().LangOpts.EnableConcisePoundFile) - return result; - for (auto &namePair : getInfoForUsedFileNames(this)) { - computeMagicFileString(this, namePair.first(), scratch); + computeFileID(this, namePair.first(), scratch); auto &infoForPaths = namePair.second; assert(!infoForPaths.empty()); // TODO: In the future, we'd like to handle these conflicts gracefully by - // generating a unique `#file` string for each conflicting name. For now, we - // will simply warn about conflicts. + // generating a unique `#fileID` string for each conflicting name. For now, + // we will simply warn about conflicts. StringRef winner = infoForPaths.begin()->first(); if (infoForPaths.size() > 1) - winner = resolveMagicNameConflicts(this, scratch, infoForPaths, - shouldDiagnose); + winner = resolveFileIDConflicts(this, scratch, infoForPaths, + shouldDiagnose); for (auto &pathPair : infoForPaths) { result[pathPair.first()] = diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index be1f849c7a282..45821a23424a8 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -24,6 +24,10 @@ bool ModuleDependencies::isSwiftModule() const { return isa(storage.get()); } +bool ModuleDependencies::isPlaceholderSwiftModule() const { + return isa(storage.get()); +} + /// Retrieve the dependencies for a Swift module. const SwiftModuleDependenciesStorage * ModuleDependencies::getAsSwiftModule() const { @@ -36,9 +40,15 @@ ModuleDependencies::getAsClangModule() const { return dyn_cast(storage.get()); } +/// Retrieve the dependencies for a placeholder dependency module stub. +const PlaceholderSwiftModuleDependencyStorage * +ModuleDependencies::getAsPlaceholderDependencyModule() const { + return dyn_cast(storage.get()); +} + void ModuleDependencies::addModuleDependency( - StringRef module, llvm::StringSet<> &alreadyAddedModules) { - if (alreadyAddedModules.insert(module).second) + StringRef module, llvm::StringSet<> *alreadyAddedModules) { + if (!alreadyAddedModules || alreadyAddedModules->insert(module).second) storage->moduleDependencies.push_back(module.str()); } @@ -53,7 +63,7 @@ void ModuleDependencies::addModuleDependencies( continue; addModuleDependency(importDecl->getModulePath().front().Item.str(), - alreadyAddedModules); + &alreadyAddedModules); } auto fileName = sf.getFilename(); @@ -102,7 +112,8 @@ ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { switch (kind) { case ModuleDependenciesKind::Swift: return SwiftModuleDependencies; - + case ModuleDependenciesKind::SwiftPlaceholder: + return PlaceholderSwiftModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -114,7 +125,8 @@ ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) const { switch (kind) { case ModuleDependenciesKind::Swift: return SwiftModuleDependencies; - + case ModuleDependenciesKind::SwiftPlaceholder: + return PlaceholderSwiftModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -126,6 +138,7 @@ bool ModuleDependenciesCache::hasDependencies( Optional kind) const { if (!kind) { return hasDependencies(moduleName, ModuleDependenciesKind::Swift) || + hasDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder) || hasDependencies(moduleName, ModuleDependenciesKind::Clang); } @@ -140,8 +153,11 @@ Optional ModuleDependenciesCache::findDependencies( if (auto swiftDep = findDependencies( moduleName, ModuleDependenciesKind::Swift)) return swiftDep; - - return findDependencies(moduleName, ModuleDependenciesKind::Clang); + else if (auto swiftPlaceholderDep = findDependencies( + moduleName, ModuleDependenciesKind::SwiftPlaceholder)) + return swiftPlaceholderDep; + else + return findDependencies(moduleName, ModuleDependenciesKind::Clang); } const auto &map = getDependenciesMap(*kind); diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index db4c5b68305a3..851cbdba9e254 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -165,6 +165,8 @@ ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx, using namespace llvm::sys; using namespace file_types; Optional modulePath; + // A map from secondary module name to a vector of overlay names. + llvm::StringMap> result; // Mimic getModuleDefiningPath() for Swift and Clang module. if (auto *swiftDep = dyn_cast(storage.get())) { // Prefer interface path to binary module path if we have it. @@ -176,12 +178,12 @@ ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx, if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { modulePath = parentDir.str(); } - } else { - modulePath = cast(storage.get())->moduleMapFile; + } else if (auto *clangDep = dyn_cast(storage.get())){ + modulePath = clangDep->moduleMapFile; assert(modulePath.hasValue()); + } else { // PlaceholderSwiftModuleDependencies + return result; } - // A map from secondary module name to a vector of overlay names. - llvm::StringMap> result; findOverlayFilesInternal(ctx, *modulePath, moduleName, SourceLoc(), [&](StringRef file) { StringRef bystandingModule; diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 626715f458227..df94b8ea9a972 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -819,13 +819,12 @@ resolveTypeDeclsToNominal(Evaluator &evaluator, SmallVectorImpl &modulesFound, bool &anyObject); -SelfBounds -SelfBoundsFromWhereClauseRequest::evaluate( +SelfBounds SelfBoundsFromWhereClauseRequest::evaluate( Evaluator &evaluator, - llvm::PointerUnion decl) const { - auto *typeDecl = decl.dyn_cast(); - auto *protoDecl = dyn_cast_or_null(typeDecl); - auto *extDecl = decl.dyn_cast(); + llvm::PointerUnion decl) const { + auto *typeDecl = decl.dyn_cast(); + auto *protoDecl = dyn_cast_or_null(typeDecl); + auto *extDecl = decl.dyn_cast(); DeclContext *dc = protoDecl ? (DeclContext *)protoDecl : (DeclContext *)extDecl; @@ -862,27 +861,22 @@ SelfBoundsFromWhereClauseRequest::evaluate( continue; // Resolve the right-hand side. - DirectlyReferencedTypeDecls rhsDecls; - if (auto typeRepr = req.getConstraintRepr()) { - rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, lookupDC); - } + if (auto *const typeRepr = req.getConstraintRepr()) { + const auto rhsNominals = getDirectlyReferencedNominalTypeDecls( + ctx, typeRepr, lookupDC, result.anyObject); - SmallVector modulesFound; - auto rhsNominals = resolveTypeDeclsToNominal(evaluator, ctx, rhsDecls, - modulesFound, - result.anyObject); - result.decls.insert(result.decls.end(), - rhsNominals.begin(), - rhsNominals.end()); + result.decls.insert(result.decls.end(), rhsNominals.begin(), + rhsNominals.end()); + } } return result; } SelfBounds swift::getSelfBoundsFromWhereClause( - llvm::PointerUnion decl) { - auto *typeDecl = decl.dyn_cast(); - auto *extDecl = decl.dyn_cast(); + llvm::PointerUnion decl) { + auto *typeDecl = decl.dyn_cast(); + auto *extDecl = decl.dyn_cast(); auto &ctx = typeDecl ? typeDecl->getASTContext() : extDecl->getASTContext(); return evaluateOrDefault(ctx.evaluator, @@ -899,21 +893,24 @@ TypeDeclsFromWhereClauseRequest::evaluate(Evaluator &evaluator, auto decls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext); result.insert(result.end(), decls.begin(), decls.end()); }; - for (const auto &req : ext->getGenericParams()->getTrailingRequirements()) { - switch (req.getKind()) { - case RequirementReprKind::TypeConstraint: - resolve(req.getSubjectRepr()); - resolve(req.getConstraintRepr()); - break; - case RequirementReprKind::SameType: - resolve(req.getFirstTypeRepr()); - resolve(req.getSecondTypeRepr()); - break; + if (auto *whereClause = ext->getTrailingWhereClause()) { + for (const auto &req : whereClause->getRequirements()) { + switch (req.getKind()) { + case RequirementReprKind::TypeConstraint: + resolve(req.getSubjectRepr()); + resolve(req.getConstraintRepr()); + break; - case RequirementReprKind::LayoutConstraint: - resolve(req.getSubjectRepr()); - break; + case RequirementReprKind::SameType: + resolve(req.getFirstTypeRepr()); + resolve(req.getSecondTypeRepr()); + break; + + case RequirementReprKind::LayoutConstraint: + resolve(req.getSubjectRepr()); + break; + } } } @@ -2137,20 +2134,31 @@ static DirectlyReferencedTypeDecls directReferencesForType(Type type) { return { }; } +TinyPtrVector swift::getDirectlyReferencedNominalTypeDecls( + ASTContext &ctx, TypeRepr *typeRepr, DeclContext *dc, bool &anyObject) { + const auto referenced = + directReferencesForTypeRepr(ctx.evaluator, ctx, typeRepr, dc); + + // Resolve those type declarations to nominal type declarations. + SmallVector modulesFound; + return resolveTypeDeclsToNominal(ctx.evaluator, ctx, referenced, modulesFound, + anyObject); +} + DirectlyReferencedTypeDecls InheritedDeclsReferencedRequest::evaluate( Evaluator &evaluator, - llvm::PointerUnion decl, + llvm::PointerUnion decl, unsigned index) const { // Prefer syntactic information when we have it. - TypeLoc &typeLoc = getInheritedTypeLocAtIndex(decl, index); + const TypeLoc &typeLoc = getInheritedTypeLocAtIndex(decl, index); if (auto typeRepr = typeLoc.getTypeRepr()) { // Figure out the context in which name lookup will occur. DeclContext *dc; - if (auto typeDecl = decl.dyn_cast()) + if (auto typeDecl = decl.dyn_cast()) dc = typeDecl->getInnermostDeclContext(); else - dc = decl.get(); + dc = (DeclContext *)decl.get(); return directReferencesForTypeRepr(evaluator, dc->getASTContext(), typeRepr, dc); @@ -2258,16 +2266,10 @@ ExtendedNominalRequest::evaluate(Evaluator &evaluator, // We must've seen 'extension { ... }' during parsing. return nullptr; - ASTContext &ctx = ext->getASTContext(); - DirectlyReferencedTypeDecls referenced = - directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext->getParent()); - // Resolve those type declarations to nominal type declarations. - SmallVector modulesFound; bool anyObject = false; - auto nominalTypes - = resolveTypeDeclsToNominal(evaluator, ctx, referenced, modulesFound, - anyObject); + const auto nominalTypes = getDirectlyReferencedNominalTypeDecls( + ext->getASTContext(), typeRepr, ext->getParent(), anyObject); // If there is more than 1 element, we will emit a warning or an error // elsewhere, so don't handle that case here. @@ -2287,6 +2289,73 @@ static bool declsAreAssociatedTypes(ArrayRef decls) { return true; } +static GenericParamList * +createExtensionGenericParams(ASTContext &ctx, + ExtensionDecl *ext, + NominalTypeDecl *nominal) { + // Collect generic parameters from all outer contexts. + SmallVector allGenericParams; + nominal->forEachGenericContext([&](GenericParamList *gpList) { + allGenericParams.push_back(gpList->clone(ext)); + }); + + GenericParamList *toParams = nullptr; + for (auto *gpList : llvm::reverse(allGenericParams)) { + gpList->setOuterParameters(toParams); + toParams = gpList; + } + + return toParams; +} + +GenericParamList * +GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) const { + if (auto *ext = dyn_cast(value)) { + // Create the generic parameter list for the extension by cloning the + // generic parameter lists of the nominal and any of its parent types. + auto &ctx = value->getASTContext(); + auto *nominal = ext->getExtendedNominal(); + if (!nominal) { + return nullptr; + } + auto *genericParams = createExtensionGenericParams(ctx, ext, nominal); + + // Protocol extensions need an inheritance clause due to how name lookup + // is implemented. + if (auto *proto = ext->getExtendedProtocolDecl()) { + auto protoType = proto->getDeclaredInterfaceType(); + TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; + genericParams->getParams().front()->setInherited( + ctx.AllocateCopy(selfInherited)); + } + + // Set the depth of every generic parameter. + unsigned depth = nominal->getGenericContextDepth(); + for (auto *outerParams = genericParams; + outerParams != nullptr; + outerParams = outerParams->getOuterParameters()) + outerParams->setDepth(depth--); + + return genericParams; + } else if (auto *proto = dyn_cast(value)) { + // The generic parameter 'Self'. + auto &ctx = value->getASTContext(); + auto selfId = ctx.Id_Self; + auto selfDecl = new (ctx) GenericTypeParamDecl( + proto, selfId, SourceLoc(), /*depth=*/0, /*index=*/0); + auto protoType = proto->getDeclaredInterfaceType(); + TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; + selfDecl->setInherited(ctx.AllocateCopy(selfInherited)); + selfDecl->setImplicit(); + + // The generic parameter list itself. + auto result = GenericParamList::create(ctx, SourceLoc(), selfDecl, + SourceLoc()); + return result; + } + return nullptr; +} + NominalTypeDecl * CustomAttrNominalRequest::evaluate(Evaluator &evaluator, CustomAttr *attr, DeclContext *dc) const { @@ -2356,12 +2425,11 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, } void swift::getDirectlyInheritedNominalTypeDecls( - llvm::PointerUnion decl, - unsigned i, - llvm::SmallVectorImpl> &result, + llvm::PointerUnion decl, + unsigned i, llvm::SmallVectorImpl> &result, bool &anyObject) { - auto typeDecl = decl.dyn_cast(); - auto extDecl = decl.dyn_cast(); + auto typeDecl = decl.dyn_cast(); + auto extDecl = decl.dyn_cast(); ASTContext &ctx = typeDecl ? typeDecl->getASTContext() : extDecl->getASTContext(); @@ -2393,10 +2461,10 @@ void swift::getDirectlyInheritedNominalTypeDecls( SmallVector, 4> swift::getDirectlyInheritedNominalTypeDecls( - llvm::PointerUnion decl, - bool &anyObject) { - auto typeDecl = decl.dyn_cast(); - auto extDecl = decl.dyn_cast(); + llvm::PointerUnion decl, + bool &anyObject) { + auto typeDecl = decl.dyn_cast(); + auto extDecl = decl.dyn_cast(); // Gather results from all of the inherited types. unsigned numInherited = typeDecl ? typeDecl->getInherited().size() @@ -2451,7 +2519,7 @@ void FindLocalVal::checkPattern(const Pattern *Pat, DeclVisibilityKind Reason) { return; case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: return checkPattern(Pat->getSemanticsProvidingPattern(), Reason); case PatternKind::Named: return checkValueDecl(cast(Pat)->getDecl(), Reason); diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index c21024b789c02..e581001fba475 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -225,18 +225,20 @@ evaluator::DependencySource GetDestructorRequest::readDependencySource( Optional GenericParamListRequest::getCachedResult() const { auto *decl = std::get<0>(getStorage()); - if (!decl->GenericParamsAndBit.getInt()) { - return None; - } - return decl->GenericParamsAndBit.getPointer(); + if (auto *params = decl->GenericParamsAndBit.getPointer()) + return params; + + if (decl->GenericParamsAndBit.getInt()) + return nullptr; + + return None; } void GenericParamListRequest::cacheResult(GenericParamList *params) const { auto *context = std::get<0>(getStorage()); - if (params) { - for (auto param : *params) - param->setDeclContext(context); - } + if (params) + params->setDeclContext(context); + context->GenericParamsAndBit.setPointerAndInt(params, true); } diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index 97178c4eb9156..2006c06e62e27 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -48,7 +48,7 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, PatternKind kind) { return OS << "prefix 'is' pattern"; case PatternKind::Expr: return OS << "expression pattern"; - case PatternKind::Var: + case PatternKind::Binding: return OS << "'var' binding pattern"; case PatternKind::EnumElement: return OS << "enum case matching pattern"; @@ -187,7 +187,6 @@ namespace { std::pair walkToStmtPre(Stmt *S) override { return { false, S }; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } bool walkToTypeReprPre(TypeRepr *T) override { return false; } bool walkToParameterListPre(ParameterList *PL) override { return false; } bool walkToDeclPre(Decl *D) override { return false; } @@ -214,7 +213,7 @@ void Pattern::forEachVariable(llvm::function_ref fn) const { case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: return getSemanticsProvidingPattern()->forEachVariable(fn); case PatternKind::Tuple: @@ -262,8 +261,8 @@ void Pattern::forEachNode(llvm::function_ref f) { return cast(this)->getSubPattern()->forEachNode(f); case PatternKind::Typed: return cast(this)->getSubPattern()->forEachNode(f); - case PatternKind::Var: - return cast(this)->getSubPattern()->forEachNode(f); + case PatternKind::Binding: + return cast(this)->getSubPattern()->forEachNode(f); case PatternKind::Tuple: for (auto elt : cast(this)->getElements()) @@ -407,15 +406,6 @@ TypedPattern::TypedPattern(Pattern *pattern, TypeRepr *tr) Bits.TypedPattern.IsPropagatedType = false; } -TypeLoc TypedPattern::getTypeLoc() const { - TypeLoc loc = TypeLoc(PatTypeRepr); - - if (hasType()) - loc.setType(getType()); - - return loc; -} - SourceLoc TypedPattern::getLoc() const { if (SubPattern->isImplicit() && PatTypeRepr) return PatTypeRepr->getSourceRange().Start; diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index c070a55d3cd8a..83d24aacaf94a 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -392,16 +392,16 @@ SourceLoc RepeatWhileStmt::getEndLoc() const { return Cond->getEndLoc(); } SourceRange CaseLabelItem::getSourceRange() const { if (auto *E = getGuardExpr()) - return { CasePattern->getStartLoc(), E->getEndLoc() }; - return CasePattern->getSourceRange(); + return { getPattern()->getStartLoc(), E->getEndLoc() }; + return getPattern()->getSourceRange(); } SourceLoc CaseLabelItem::getStartLoc() const { - return CasePattern->getStartLoc(); + return getPattern()->getStartLoc(); } SourceLoc CaseLabelItem::getEndLoc() const { if (auto *E = getGuardExpr()) return E->getEndLoc(); - return CasePattern->getEndLoc(); + return getPattern()->getEndLoc(); } CaseStmt::CaseStmt(CaseParentKind parentKind, SourceLoc itemIntroducerLoc, @@ -458,6 +458,40 @@ CaseStmt *CaseStmt::create(ASTContext &ctx, CaseParentKind ParentKind, body, caseVarDecls, implicit, fallthroughStmt); } +namespace { + +template +CaseStmt *findNextCaseStmt( + CaseIterator first, CaseIterator last, const CaseStmt *caseStmt) { + for(auto caseIter = first; caseIter != last; ++caseIter) { + if (*caseIter == caseStmt) { + ++caseIter; + return caseIter == last ? nullptr : *caseIter; + } + } + + return nullptr; +} + +} + +CaseStmt *CaseStmt::findNextCaseStmt() const { + auto parent = getParentStmt(); + if (!parent) + return nullptr; + + if (auto switchParent = dyn_cast(parent)) { + return ::findNextCaseStmt( + switchParent->getCases().begin(), switchParent->getCases().end(), + this); + } + + auto doCatchParent = cast(parent); + return ::findNextCaseStmt( + doCatchParent->getCatches().begin(), doCatchParent->getCatches().end(), + this); +} + SwitchStmt *SwitchStmt::create(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc, Expr *SubjectExpr, SourceLoc LBraceLoc, diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index fea4ffc0a0b4d..7653cc6a29961 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -337,7 +337,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { for (auto reqt : genericSig->getRequirements()) { if (reqt.getKind() == RequirementKind::Conformance) { if (reqt.getFirstType()->isEqual(type) && - reqt.getSecondType()->isEqual(proto->getDeclaredType())) + reqt.getSecondType()->isEqual(proto->getDeclaredInterfaceType())) return getConformances()[index]; ++index; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 4c59a9f812fa5..62142eb9ccb27 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -337,6 +337,10 @@ bool TypeBase::isObjCExistentialType() { return getCanonicalType().isObjCExistentialType(); } +bool TypeBase::isTypeErasedGenericClassType() { + return getCanonicalType().isTypeErasedGenericClassType(); +} + bool CanType::isObjCExistentialTypeImpl(CanType type) { if (!type.isExistentialType()) return false; @@ -832,6 +836,32 @@ Type TypeBase::replaceCovariantResultType(Type newResultType, return FunctionType::get(inputType, resultType, fnType->getExtInfo()); } +/// Whether this parameter accepts an unlabeled trailing closure argument +/// using the more-restrictive forward-scan rule. +static bool allowsUnlabeledTrailingClosureParameter(const ParamDecl *param) { + // inout parameters never allow an unlabeled trailing closure. + if (param->isInOut()) + return false; + + Type paramType = param->isVariadic() ? param->getVarargBaseTy() + : param->getInterfaceType(); + paramType = paramType->getRValueType()->lookThroughAllOptionalTypes(); + + // For autoclosure parameters, look through the autoclosure result type + // to get the actual argument type. + if (param->isAutoClosure()) { + auto fnType = paramType->getAs(); + if (!fnType) + return false; + + paramType = fnType->getResult()->lookThroughAllOptionalTypes(); + } + + // After lookup through all optional types, this parameter allows an + // unlabeled trailing closure if it is (structurally) a function type. + return paramType->is(); +} + ParameterListInfo::ParameterListInfo( ArrayRef params, const ValueDecl *paramOwner, @@ -841,7 +871,7 @@ ParameterListInfo::ParameterListInfo( // No parameter owner means no parameter list means no default arguments // - hand back the zeroed bitvector. // - // FIXME: We ought to not request default argument info in this case. + // FIXME: We ought to not request paramer list info in this case. if (!paramOwner) return; @@ -850,14 +880,8 @@ ParameterListInfo::ParameterListInfo( return; // Find the corresponding parameter list. - const ParameterList *paramList = nullptr; - if (auto *func = dyn_cast(paramOwner)) { - paramList = func->getParameters(); - } else if (auto *subscript = dyn_cast(paramOwner)) { - paramList = subscript->getIndices(); - } else if (auto *enumElement = dyn_cast(paramOwner)) { - paramList = enumElement->getParameterList(); - } + const ParameterList *paramList = + getParameterList(const_cast(paramOwner)); // No parameter list means no default arguments - hand back the zeroed // bitvector. @@ -875,12 +899,21 @@ ParameterListInfo::ParameterListInfo( if (params.size() != paramList->size()) return; - // Note which parameters have default arguments and/or function builders. + // Now we have enough information to determine which parameters accept + // unlabled trailing closures. + acceptsUnlabeledTrailingClosures.resize(params.size()); + + // Note which parameters have default arguments and/or accept unlabeled + // trailing closure arguments with the forward-scan rule. for (auto i : range(0, params.size())) { auto param = paramList->get(i); if (param->isDefaultArgument()) { defaultArguments.set(i); } + + if (allowsUnlabeledTrailingClosureParameter(param)) { + acceptsUnlabeledTrailingClosures.set(i); + } } } @@ -889,6 +922,12 @@ bool ParameterListInfo::hasDefaultArgument(unsigned paramIdx) const { : false; } +bool ParameterListInfo::acceptsUnlabeledTrailingClosureArgument( + unsigned paramIdx) const { + return paramIdx >= acceptsUnlabeledTrailingClosures.size() || + acceptsUnlabeledTrailingClosures[paramIdx]; +} + /// Turn a param list into a symbolic and printable representation that does not /// include the types, something like (_:, b:, c:) std::string swift::getParamListAsString(ArrayRef params) { @@ -996,8 +1035,8 @@ addMinimumProtocols(Type T, SmallVectorImpl &Protocols, if (Visited.insert(Proto->getDecl()).second) { Stack.push_back(Proto->getDecl()); for (auto Inherited : Proto->getDecl()->getInheritedProtocols()) - addMinimumProtocols(Inherited->getDeclaredType(), Protocols, Known, - Visited, Stack, ZappedAny); + addMinimumProtocols(Inherited->getDeclaredInterfaceType(), + Protocols, Known, Visited, Stack, ZappedAny); } return; } @@ -1073,8 +1112,8 @@ void ProtocolType::canonicalizeProtocols( // Add the protocols we inherited. for (auto Inherited : Current->getInheritedProtocols()) { - addMinimumProtocols(Inherited->getDeclaredType(), protocols, known, - visited, stack, zappedAny); + addMinimumProtocols(Inherited->getDeclaredInterfaceType(), + protocols, known, visited, stack, zappedAny); } } @@ -1208,9 +1247,7 @@ CanType TypeBase::computeCanonicalType() { getCanonicalParams(funcTy, genericSig, canParams); auto resultTy = funcTy->getResult()->getCanonicalType(genericSig); - bool useClangFunctionType = - resultTy->getASTContext().LangOpts.UseClangFunctionTypes; - auto extInfo = funcTy->getCanonicalExtInfo(useClangFunctionType); + auto extInfo = funcTy->getCanonicalExtInfo(useClangTypes(resultTy)); if (genericSig) { Result = GenericFunctionType::get(genericSig, canParams, resultTy, extInfo); @@ -1748,7 +1785,7 @@ class IsBindableVisitor CanType visitFunctionType(FunctionType *func, CanType subst, ArchetypeType*, ArrayRef) { if (auto substFunc = dyn_cast(subst)) { - if (func->getExtInfo() != substFunc->getExtInfo()) + if (!func->hasSameExtInfoAs(substFunc)) return CanType(); if (func->getParams().size() != substFunc->getParams().size()) @@ -1787,7 +1824,7 @@ class IsBindableVisitor CanType visitSILFunctionType(SILFunctionType *func, CanType subst, ArchetypeType*, ArrayRef) { if (auto substFunc = dyn_cast(subst)) { - if (func->getExtInfo() != substFunc->getExtInfo()) + if (!func->hasSameExtInfoAs(substFunc)) return CanType(); if (func->getInvocationGenericSignature() @@ -2231,8 +2268,9 @@ getForeignRepresentable(Type type, ForeignLanguage language, // Function types. if (auto functionType = type->getAs()) { - // Cannot handle throwing functions. - if (functionType->getExtInfo().throws()) + // Cannot handle async or throwing functions. + if (functionType->getExtInfo().isAsync() || + functionType->getExtInfo().isThrowing()) return failure(); // Whether we have found any types that are bridged. @@ -2355,7 +2393,8 @@ getForeignRepresentable(Type type, ForeignLanguage language, if (wasOptional) { if (nominal->hasClangNode()) { Type underlyingType = - nominal->getDeclaredType()->getSwiftNewtypeUnderlyingType(); + nominal->getDeclaredInterfaceType() + ->getSwiftNewtypeUnderlyingType(); if (underlyingType) { return getForeignRepresentable(OptionalType::get(underlyingType), language, dc); @@ -2568,7 +2607,7 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2, auto ext1 = fn1->getExtInfo(); auto ext2 = fn2->getExtInfo(); if (matchMode.contains(TypeMatchFlags::AllowOverride)) { - if (ext2.throws()) { + if (ext2.isThrowing()) { ext1 = ext1.withThrows(true); } } @@ -2582,7 +2621,7 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2, if (!ext2.isNoEscape()) ext1 = ext1.withNoEscape(false); } - if (ext1 != ext2) + if (!ext1.isEqualTo(ext2, useClangTypes(fn1))) return false; return paramsAndResultMatch(); @@ -2805,7 +2844,7 @@ Type ArchetypeType::getExistentialType() const { constraintTypes.push_back(super); } for (auto proto : getConformsTo()) { - constraintTypes.push_back(proto->getDeclaredType()); + constraintTypes.push_back(proto->getDeclaredInterfaceType()); } return ProtocolCompositionType::get( const_cast(this)->getASTContext(), constraintTypes, false); @@ -3357,7 +3396,7 @@ Type ProtocolCompositionType::get(const ASTContext &C, // If one protocol remains with no further constraints, its nominal // type is the canonical type. if (Protocols.size() == 1 && !Superclass && !HasExplicitAnyObject) - return Protocols.front()->getDeclaredType(); + return Protocols.front()->getDeclaredInterfaceType(); // Form the set of canonical protocol types from the protocol // declarations, and use that to build the canonical composition type. @@ -3367,7 +3406,7 @@ Type ProtocolCompositionType::get(const ASTContext &C, std::transform(Protocols.begin(), Protocols.end(), std::back_inserter(CanTypes), [](ProtocolDecl *Proto) { - return Proto->getDeclaredType(); + return Proto->getDeclaredInterfaceType(); }); // TODO: Canonicalize away HasExplicitAnyObject if it is implied @@ -3375,47 +3414,33 @@ 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 - if (!(type->isFunctionPointerType() || type->isBlockPointerType() || - type->isFunctionReferenceType())) { - SmallString<256> buf; - llvm::raw_svector_ostream os(buf); - os << "Expected a Clang function type wrapped in a pointer type or " - << "a block pointer type but found:\n"; - type->dump(os); - llvm_unreachable(os.str().data()); - } -#endif - return; -} - -const clang::Type *AnyFunctionType::getClangFunctionType() const { +ClangTypeInfo AnyFunctionType::getClangTypeInfo() const { switch (getKind()) { case TypeKind::Function: - return cast(this)->getClangFunctionType(); + return cast(this)->getClangTypeInfo(); case TypeKind::GenericFunction: // Generic functions do not have C types. - return nullptr; + return ClangTypeInfo(); default: llvm_unreachable("Illegal type kind for AnyFunctionType."); } } -const clang::Type *AnyFunctionType::getCanonicalClangFunctionType() const { - auto *ty = getClangFunctionType(); - return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; +ClangTypeInfo AnyFunctionType::getCanonicalClangTypeInfo() const { + return getClangTypeInfo().getCanonical(); } -// TODO: [store-sil-clang-function-type] -const clang::FunctionType *SILFunctionType::getClangFunctionType() const { - return nullptr; +bool AnyFunctionType::hasSameExtInfoAs(const AnyFunctionType *otherFn) { + return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); +} + +// [TODO: Store-SIL-Clang-type] +ClangTypeInfo SILFunctionType::getClangTypeInfo() const { + return ClangTypeInfo(); +} + +bool SILFunctionType::hasSameExtInfoAs(const SILFunctionType *otherFn) { + return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); } FunctionType * @@ -4277,7 +4302,14 @@ case TypeKind::Id: case TypeKind::SILFunction: { auto fnTy = cast(base); bool changed = false; - + auto hasTypeErasedGenericClassType = [](Type ty) -> bool { + return ty.findIf([](Type subType) -> bool { + if (subType->isTypeErasedGenericClassType()) + return true; + else + return false; + }); + }; auto updateSubs = [&](SubstitutionMap &subs) -> bool { // This interface isn't suitable for updating the substitution map in a // substituted SILFunctionType. @@ -4287,9 +4319,8 @@ case TypeKind::Id: auto transformed = type.transformRec(fn); assert((type->isEqual(transformed) || (type->hasTypeParameter() && transformed->hasTypeParameter()) || - (type->getCanonicalType().isTypeErasedGenericClassType() && - transformed->getCanonicalType() - .isTypeErasedGenericClassType())) && + (hasTypeErasedGenericClassType(type) && + hasTypeErasedGenericClassType(transformed))) && "Substituted SILFunctionType can't be transformed into a " "concrete type"); newReplacements.push_back(transformed->getCanonicalType()); @@ -5063,8 +5094,11 @@ AnyFunctionType *AnyFunctionType::getWithoutDifferentiability() const { param.getParameterFlags().withNoDerivative(false)); newParams.push_back(newParam); } - auto nonDiffExtInfo = getExtInfo() - .withDifferentiabilityKind(DifferentiabilityKind::NonDifferentiable); + auto nonDiffExtInfo = + getExtInfo() + .intoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::NonDifferentiable) + .build(); if (isa(this)) return FunctionType::get(newParams, getResult(), nonDiffExtInfo); assert(isa(this)); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 12887f1be18e7..ff2efbc44ce89 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -37,14 +37,14 @@ namespace swift { } void swift::simple_display( - llvm::raw_ostream &out, - const llvm::PointerUnion &value) { - if (auto type = value.dyn_cast()) { + llvm::raw_ostream &out, + const llvm::PointerUnion &value) { + if (auto type = value.dyn_cast()) { type->dumpRef(out); return; } - auto ext = value.get(); + auto ext = value.get(); simple_display(out, ext); } @@ -116,7 +116,7 @@ void InheritedTypeRequest::cacheResult(Type value) const { const auto &storage = getStorage(); auto &typeLoc = getInheritedTypeLocAtIndex(std::get<0>(storage), std::get<1>(storage)); - typeLoc.setType(value); + const_cast(typeLoc).setType(value); } //----------------------------------------------------------------------------// diff --git a/lib/AST/TypeJoinMeet.cpp b/lib/AST/TypeJoinMeet.cpp index a1af594424502..a5609f8b669fb 100644 --- a/lib/AST/TypeJoinMeet.cpp +++ b/lib/AST/TypeJoinMeet.cpp @@ -314,7 +314,7 @@ CanType TypeJoin::visitFunctionType(CanType second) { auto secondExtInfo = secondFnTy->getExtInfo(); // FIXME: Properly handle these attributes. - if (firstExtInfo != secondExtInfo) + if (!firstExtInfo.isEqualTo(secondExtInfo, useClangTypes(First))) return Unimplemented; if (!AnyFunctionType::equalParams(firstFnTy->getParams(), diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 61cfbee4f91bd..0a28f865f8e88 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -221,7 +221,7 @@ void FunctionTypeRepr::printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const { Printer.callPrintStructurePre(PrintStructureKind::FunctionType); printTypeRepr(ArgsTy, Printer, Opts); - if (throws()) { + if (isThrowing()) { Printer << " "; Printer.printKeyword("throws", Opts); } diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 47bee7793ac61..1f649853bd9b7 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -12,7 +12,12 @@ else() endif() function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file("${dir}" ${name}_vc) + if(SWIFT_APPEND_VC_REV) + # generate_vcs_version_script generates header with only `undef`s + # inside when source directory doesn't exist. + find_first_existing_vc_file("${dir}" ${name}_vc) + set(dir_when_append_enabled ${dir}) + endif() # Create custom target to generate the VC revision include. set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") @@ -22,7 +27,7 @@ function(generate_revision_inc revision_inc_var name dir) add_custom_command(OUTPUT "${version_inc}" DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" COMMAND ${CMAKE_COMMAND} "-DNAMES=$" - "-D$_SOURCE_DIR=${dir}" + "-D$_SOURCE_DIR=${dir_when_append_enabled}" "-DHEADER_FILE=${version_inc}" -P "${generate_vcs_version_script}") @@ -62,7 +67,6 @@ add_swift_host_library(swiftBasic STATIC StringExtras.cpp TaskQueue.cpp ThreadSafeRefCounted.cpp - Timer.cpp Unicode.cpp UUID.cpp Version.cpp diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 6761891b8dcce..c90a1d95b7243 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -131,23 +131,6 @@ DarwinPlatformKind swift::getDarwinPlatformKind(const llvm::Triple &triple) { llvm_unreachable("Unsupported Darwin platform"); } -DarwinPlatformKind swift::getNonSimulatorPlatform(DarwinPlatformKind platform) { - switch (platform) { - case DarwinPlatformKind::MacOS: - return DarwinPlatformKind::MacOS; - case DarwinPlatformKind::IPhoneOS: - case DarwinPlatformKind::IPhoneOSSimulator: - return DarwinPlatformKind::IPhoneOS; - case DarwinPlatformKind::TvOS: - case DarwinPlatformKind::TvOSSimulator: - return DarwinPlatformKind::TvOS; - case DarwinPlatformKind::WatchOS: - case DarwinPlatformKind::WatchOSSimulator: - return DarwinPlatformKind::WatchOS; - } - llvm_unreachable("Unsupported Darwin platform"); -} - static StringRef getPlatformNameForDarwin(const DarwinPlatformKind platform) { switch (platform) { case DarwinPlatformKind::MacOS: @@ -451,3 +434,58 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( return None; } + + +/// Remap the given version number via the version map, or produce \c None if +/// there is no mapping for this version. +static Optional remapVersion( + const llvm::StringMap &versionMap, + llvm::VersionTuple version) { + // The build number is never used in the lookup. + version = version.withoutBuild(); + + // Look for this specific version. + auto known = versionMap.find(version.getAsString()); + if (known != versionMap.end()) + return known->second; + + // If an extra ".0" was specified (in the subminor version), drop that + // and look again. + if (!version.getSubminor() || *version.getSubminor() != 0) + return None; + + version = llvm::VersionTuple(version.getMajor(), *version.getMinor()); + known = versionMap.find(version.getAsString()); + if (known != versionMap.end()) + return known->second; + + // If another extra ".0" wa specified (in the minor version), drop that + // and look again. + if (!version.getMinor() || *version.getMinor() != 0) + return None; + + version = llvm::VersionTuple(version.getMajor()); + known = versionMap.find(version.getAsString()); + if (known != versionMap.end()) + return known->second; + + return None; +} + +llvm::VersionTuple +swift::getTargetSDKVersion(clang::driver::DarwinSDKInfo &SDKInfo, + const llvm::Triple &triple) { + // Retrieve the SDK version. + auto SDKVersion = SDKInfo.getVersion(); + + // For the Mac Catalyst environment, we have a macOS SDK with a macOS + // SDK version. Map that to the corresponding iOS version number to pass + // down to the linker. + if (tripleIsMacCatalystEnvironment(triple)) { + return remapVersion( + SDKInfo.getVersionMap().MacOS2iOSMacMapping, SDKVersion) + .getValueOr(llvm::VersionTuple(0, 0, 0)); + } + + return SDKVersion; +} diff --git a/lib/Basic/Statistic.cpp b/lib/Basic/Statistic.cpp index 762e31050ca8c..d05b9767572cd 100644 --- a/lib/Basic/Statistic.cpp +++ b/lib/Basic/Statistic.cpp @@ -14,7 +14,6 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" -#include "swift/Basic/Timer.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "llvm/ADT/DenseMap.h" @@ -146,9 +145,8 @@ auxName(StringRef ModuleName, } class UnifiedStatsReporter::RecursionSafeTimers { - struct RecursionSafeTimer { - llvm::Optional Timer; + llvm::Optional Timer; size_t RecursionDepth; }; @@ -159,7 +157,7 @@ class UnifiedStatsReporter::RecursionSafeTimers { void beginTimer(StringRef Name) { RecursionSafeTimer &T = Timers[Name]; if (T.RecursionDepth == 0) { - T.Timer.emplace(Name); + T.Timer.emplace(Name, Name, "swift", "Swift compilation"); } ++T.RecursionDepth; } @@ -354,7 +352,6 @@ UnifiedStatsReporter::UnifiedStatsReporter(StringRef ProgramName, path::append(TraceFilename, makeTraceFileName(ProgramName, AuxName)); path::append(ProfileDirname, makeProfileDirName(ProgramName, AuxName)); EnableStatistics(/*PrintOnExit=*/false); - SharedTimer::enableCompilationTimers(); if (TraceEvents || ProfileEvents || ProfileEntities) LastTracedFrontendCounters.emplace(); if (TraceEvents) diff --git a/lib/Basic/Unicode.cpp b/lib/Basic/Unicode.cpp index a299c457a7805..e7479be57820f 100644 --- a/lib/Basic/Unicode.cpp +++ b/lib/Basic/Unicode.cpp @@ -123,22 +123,3 @@ unsigned swift::unicode::extractFirstUnicodeScalar(StringRef S) { (void)Result; return Scalar; } - -uint64_t swift::unicode::getUTF16Length(StringRef Str) { - uint64_t Length; - // Transcode the string to UTF-16 to get its length. - SmallVector buffer(Str.size() + 1); // +1 for ending nulls. - const llvm::UTF8 *fromPtr = (const llvm::UTF8 *) Str.data(); - llvm::UTF16 *toPtr = &buffer[0]; - llvm::ConversionResult Result = - ConvertUTF8toUTF16(&fromPtr, fromPtr + Str.size(), - &toPtr, toPtr + Str.size(), - llvm::strictConversion); - assert(Result == llvm::conversionOK && - "UTF-8 encoded string cannot be converted into UTF-16 encoding"); - (void)Result; - - // The length of the transcoded string in UTF-16 code points. - Length = toPtr - &buffer[0]; - return Length; -} diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index 37ccda1de0bce..6ab89f46fd310 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -405,7 +405,7 @@ std::string getSwiftFullVersion(Version effectiveVersion) { OS << "-dev"; #endif - if (!(effectiveVersion == Version::getCurrentLanguageVersion())) { + if (effectiveVersion != Version::getCurrentLanguageVersion()) { OS << " effective-" << effectiveVersion; } diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 97c8bfc6302d3..a9076c210e033 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -24,6 +24,7 @@ add_swift_host_library(swiftClangImporter STATIC target_link_libraries(swiftClangImporter PRIVATE swiftAST swiftParse + clangTooling LLVMBitstreamReader) target_link_libraries(swiftClangImporter INTERFACE diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 6aba62af16e69..19f6dc6c0c97e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -734,22 +734,22 @@ importer::addCommonInvocationArguments( } else if (triple.isOSDarwin()) { // Special case CPU based on known deployments: - // - arm64 deploys to cyclone + // - arm64 deploys to apple-a7 // - arm64 on macOS // - arm64 for iOS/tvOS/watchOS simulators - // - arm64e deploys to vortex - // and arm64e (everywhere) and arm64e macOS defaults to the "vortex" CPU + // - arm64e deploys to apple-a12 + // and arm64e (everywhere) and arm64e macOS defaults to the "apple-a12" CPU // for Darwin, but Clang only detects this if we use -arch. if (triple.getArchName() == "arm64e") - invocationArgStrs.push_back("-mcpu=vortex"); + invocationArgStrs.push_back("-mcpu=apple-a12"); else if (triple.isAArch64() && triple.isMacOSX()) - invocationArgStrs.push_back("-mcpu=vortex"); + invocationArgStrs.push_back("-mcpu=apple-a12"); else if (triple.isAArch64() && triple.isSimulatorEnvironment() && (triple.isiOS() || triple.isWatchOS())) - invocationArgStrs.push_back("-mcpu=vortex"); + invocationArgStrs.push_back("-mcpu=apple-a12"); else if (triple.getArch() == llvm::Triple::aarch64 || triple.getArch() == llvm::Triple::aarch64_be) { - invocationArgStrs.push_back("-mcpu=cyclone"); + invocationArgStrs.push_back("-mcpu=apple-a7"); } } else if (triple.getArch() == llvm::Triple::systemz) { invocationArgStrs.push_back("-march=z13"); @@ -1740,10 +1740,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( auto &diagClient = static_cast(rawDiagClient); auto loadModule = [&](clang::ModuleIdPath path, - bool makeVisible) -> clang::ModuleLoadResult { - clang::Module::NameVisibilityKind visibility = - makeVisible ? clang::Module::AllVisible : clang::Module::Hidden; - + clang::Module::NameVisibilityKind visibility) + -> clang::ModuleLoadResult { auto importRAII = diagClient.handleImport(clangPath.front().first, importLoc); @@ -1769,17 +1767,24 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( clangFEOpts.IndexStorePath = preservedIndexStorePathOption; } - if (result && makeVisible) + if (result && (visibility == clang::Module::AllVisible)) { getClangPreprocessor().makeModuleVisible(result, clangImportLoc); + } return result; }; // Now load the top-level module, so that we can check if the submodule // exists without triggering a fatal error. - clangModule = loadModule(clangPath.front(), false); + clangModule = loadModule(clangPath.front(), clang::Module::AllVisible); if (!clangModule) return nullptr; + // If we're asked to import the top-level module then we're done here. + auto *topSwiftModule = finishLoadingClangModule(clangModule, importLoc); + if (path.size() == 1) { + return topSwiftModule; + } + // Verify that the submodule exists. clang::Module *submodule = clangModule; for (auto &component : path.slice(1)) { @@ -1791,7 +1796,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // put the Clang AST in a fatal error state if it /doesn't/ exist. if (!submodule && component.Item.str() == "Private" && (&component) == (&path[1])) { - submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); + submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), + clang::Module::Hidden); } if (!submodule) { @@ -1801,7 +1807,7 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( } // Finally, load the submodule and make it visible. - clangModule = loadModule(clangPath, true); + clangModule = loadModule(clangPath, clang::Module::AllVisible); if (!clangModule) return nullptr; @@ -3413,6 +3419,8 @@ void ClangModuleUnit::getImportedModules( if (filter.containsOnly(ModuleDecl::ImportFilterKind::ImplementationOnly)) return; + // [NOTE: Pure-Clang-modules-privately-import-stdlib]: + // Needed for implicitly synthesized conformances. if (filter.contains(ModuleDecl::ImportFilterKind::Private)) if (auto stdlib = owner.getStdlibModule()) imports.push_back({ModuleDecl::AccessPathTy(), stdlib}); diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index e48f784ea9bc4..6c44002d46dbb 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -265,7 +265,7 @@ void ClangImporter::recordModuleDependencies( swiftArgs, fileDeps); for (const auto &moduleName : clangModuleDep.ClangModuleDeps) { - dependencies.addModuleDependency(moduleName.ModuleName, alreadyAddedModules); + dependencies.addModuleDependency(moduleName.ModuleName, &alreadyAddedModules); } cache.recordDependencies(clangModuleDep.ModuleName, diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 7848a683f7fd4..a11f54139cc20 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -118,7 +118,9 @@ ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( decl->addFile(*wrapperUnit); // Force load overlay modules for all imported modules. - (void) namelookup::getAllImports(decl); + assert(namelookup::getAllImports(decl).size() == 1 && + namelookup::getAllImports(decl).front().importedModule == decl && + "DWARF module depends on additional modules?"); // Register the module with the ASTContext so it is available for lookups. if (!SwiftContext.getLoadedModule(name)) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index af7fd77e1597f..e35b0aed470b7 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -180,6 +180,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, return FuncDecl::create(ctx, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, funcLoc, name, nameLoc, + /*Async=*/false, /*AsyncLoc=*/SourceLoc(), throws, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, bodyParams, @@ -551,7 +552,7 @@ synthesizeEnumRawValueGetterBody(AbstractFunctionDecl *afd, void *context) { auto getterDecl = cast(afd); auto enumDecl = static_cast(context); auto rawTy = enumDecl->getRawType(); - auto enumTy = enumDecl->getDeclaredType(); + auto enumTy = enumDecl->getDeclaredInterfaceType(); ASTContext &ctx = getterDecl->getASTContext(); auto *selfDecl = getterDecl->getImplicitSelfDecl(); @@ -1634,10 +1635,10 @@ static void makeStructRawValuedWithBridge( makeComputed(computedVar, computedVarGetter, nullptr); // Create a pattern binding to describe the variable. - Pattern *computedVarPattern = createTypedNamedPattern(computedVar); + Pattern *computedBindingPattern = createTypedNamedPattern(computedVar); auto *computedPatternBinding = PatternBindingDecl::createImplicit( - ctx, StaticSpellingKind::None, computedVarPattern, /*InitExpr*/ nullptr, - structDecl); + ctx, StaticSpellingKind::None, computedBindingPattern, + /*InitExpr*/ nullptr, structDecl); auto init = createRawValueBridgingConstructor(Impl, structDecl, computedVar, storedVar, @@ -1891,7 +1892,7 @@ static bool addErrorDomain(NominalTypeDecl *swiftDecl, auto &C = importer.SwiftContext; auto swiftValueDecl = dyn_cast_or_null( importer.importDecl(errorDomainDecl, importer.CurrentVersion)); - auto stringTy = C.getStringDecl()->getDeclaredType(); + auto stringTy = C.getStringDecl()->getDeclaredInterfaceType(); assert(stringTy && "no string type available"); if (!swiftValueDecl || !swiftValueDecl->getInterfaceType()->isEqual(stringTy)) { // Couldn't actually import it as an error enum, fall back to enum @@ -3454,20 +3455,19 @@ namespace { result->setHasUnreferenceableStorage(hasUnreferenceableStorage); if (auto cxxRecordDecl = dyn_cast(decl)) { - result->setIsCxxNotTriviallyCopyable( - !cxxRecordDecl->isTriviallyCopyable()); + result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable()); for (auto ctor : cxxRecordDecl->ctors()) { if (ctor->isCopyConstructor() && (ctor->isDeleted() || ctor->getAccess() != clang::AS_public)) { - result->setIsCxxNotTriviallyCopyable(true); + result->setIsCxxNonTrivial(true); break; } } if (auto dtor = cxxRecordDecl->getDestructor()) { if (dtor->isDeleted() || dtor->getAccess() != clang::AS_public) { - result->setIsCxxNotTriviallyCopyable(true); + result->setIsCxxNonTrivial(true); } } } @@ -6961,7 +6961,8 @@ void SwiftDeclConverter::importObjCProtocols( if (auto proto = castIgnoringCompatibilityAlias( Impl.importDecl(*cp, getActiveSwiftVersion()))) { addProtocols(proto, protocols, knownProtocols); - inheritedTypes.push_back(TypeLoc::withoutLoc(proto->getDeclaredType())); + inheritedTypes.push_back( + TypeLoc::withoutLoc(proto->getDeclaredInterfaceType())); } } @@ -7012,7 +7013,8 @@ Optional SwiftDeclConverter::importObjCGenericParams( if (!proto) { return None; } - inherited.push_back(TypeLoc::withoutLoc(proto->getDeclaredType())); + inherited.push_back( + TypeLoc::withoutLoc(proto->getDeclaredInterfaceType())); } } if (inherited.empty()) { diff --git a/lib/ClangImporter/ImportMacro.cpp b/lib/ClangImporter/ImportMacro.cpp index d4befac1afc02..1e98f9d7fb7b6 100644 --- a/lib/ClangImporter/ImportMacro.cpp +++ b/lib/ClangImporter/ImportMacro.cpp @@ -558,38 +558,38 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl, } else if (tokenI[1].is(clang::tok::pipepipe)) { bool result = firstValue.getBoolValue() || secondValue.getBoolValue(); resultValue = llvm::APSInt::get(result); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Logical AND. } else if (tokenI[1].is(clang::tok::ampamp)) { bool result = firstValue.getBoolValue() && secondValue.getBoolValue(); resultValue = llvm::APSInt::get(result); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Equality. } else if (tokenI[1].is(clang::tok::equalequal)) { resultValue = llvm::APSInt::get(firstValue == secondValue); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Less than. } else if (tokenI[1].is(clang::tok::less)) { resultValue = llvm::APSInt::get(firstValue < secondValue); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Less than or equal. } else if (tokenI[1].is(clang::tok::lessequal)) { resultValue = llvm::APSInt::get(firstValue <= secondValue); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Greater than. } else if (tokenI[1].is(clang::tok::greater)) { resultValue = llvm::APSInt::get(firstValue > secondValue); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Greater than or equal. } else if (tokenI[1].is(clang::tok::greaterequal)) { resultValue = llvm::APSInt::get(firstValue >= secondValue); - resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); // Unhandled operators. } else { diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 5a2cde3d6503d..15da7f4e5ed9b 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -167,11 +167,13 @@ namespace { return {FunctionType::get( funcTy->getParams(), funcTy->getResult(), funcTy->getExtInfo() + .intoBuilder() .withRepresentation( AnyFunctionType::Representation::CFunctionPointer) - .withClangFunctionType(&type)), + .withClangFunctionType(&type) + .build()), type.isReferenceType() ? ImportHint::None - : ImportHint::CFunctionPointer}; + : ImportHint::CFunctionPointer}; } static ImportResult importOverAlignedFunctionPointerLikeType( @@ -180,7 +182,7 @@ namespace { if (!opaquePointer) { return Type(); } - return {opaquePointer->getDeclaredType(), + return {opaquePointer->getDeclaredInterfaceType(), type.isReferenceType() ? ImportHint::None : ImportHint::OtherPointer}; } @@ -417,7 +419,7 @@ namespace { : Impl.SwiftContext.getUnsafeMutableRawPointerDecl()); if (!pointerTypeDecl) return Type(); - return {pointerTypeDecl->getDeclaredType(), + return {pointerTypeDecl->getDeclaredInterfaceType(), ImportHint::OtherPointer}; } @@ -964,7 +966,7 @@ namespace { memberTypes.push_back(superclassType); for (auto protocolDecl : typeParam->getConformingProtocols()) - memberTypes.push_back(protocolDecl->getDeclaredType()); + memberTypes.push_back(protocolDecl->getDeclaredInterfaceType()); bool hasExplicitAnyObject = false; if (memberTypes.empty()) @@ -980,7 +982,7 @@ namespace { importedType = BoundGenericClassType::get( imported, nullptr, importedTypeArgs); } else { - importedType = imported->getDeclaredType(); + importedType = imported->getDeclaredInterfaceType(); } if (!type->qual_empty()) { @@ -1028,7 +1030,7 @@ namespace { if (auto objcClassDef = objcClass->getDefinition()) bridgedType = mapSwiftBridgeAttr(objcClassDef); else if (objcClass->getName() == "NSString") - bridgedType = Impl.SwiftContext.getStringDecl()->getDeclaredType(); + bridgedType = Impl.SwiftContext.getStringDecl()->getDeclaredInterfaceType(); if (bridgedType) { // Gather the type arguments. @@ -1083,7 +1085,7 @@ namespace { keyStructDecl == Impl.SwiftContext.getDictionaryDecl() || keyStructDecl == Impl.SwiftContext.getArrayDecl()) { if (auto anyHashable = Impl.SwiftContext.getAnyHashableDecl()) - keyType = anyHashable->getDeclaredType(); + keyType = anyHashable->getDeclaredInterfaceType(); else keyType = Type(); } @@ -1124,7 +1126,7 @@ namespace { if (!proto) return Type(); - members.push_back(proto->getDeclaredType()); + members.push_back(proto->getDeclaredInterfaceType()); } importedType = ProtocolCompositionType::get(Impl.SwiftContext, @@ -1409,14 +1411,14 @@ static ImportedType adjustTypeForConcreteImport( // Turn BOOL and DarwinBoolean into Bool in contexts that can bridge types // losslessly. if (bridging == Bridgeability::Full && canBridgeTypes(importKind)) - importedType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); + importedType = impl.SwiftContext.getBoolDecl()->getDeclaredInterfaceType(); break; case ImportHint::NSUInteger: // When NSUInteger is used as an enum's underlying type or if it does not // come from a system module, make sure it stays unsigned. if (importKind == ImportTypeKind::Enum || !allowNSUIntegerAsInt) - importedType = impl.SwiftContext.getUIntDecl()->getDeclaredType(); + importedType = impl.SwiftContext.getUIntDecl()->getDeclaredInterfaceType(); break; case ImportHint::CFPointer: @@ -1647,7 +1649,7 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( switch (getClangASTContext().BuiltinInfo.getTypeString(builtinID)[0]) { case 'z': // size_t case 'Y': // ptrdiff_t - return {SwiftContext.getIntDecl()->getDeclaredType(), false}; + return {SwiftContext.getIntDecl()->getDeclaredInterfaceType(), false}; default: break; } @@ -2462,7 +2464,7 @@ Type ClangImporter::Implementation::getNSObjectType() { return NSObjectTy; if (auto decl = dyn_cast_or_null(importDeclByName("NSObject"))) { - NSObjectTy = decl->getDeclaredType(); + NSObjectTy = decl->getDeclaredInterfaceType(); return NSObjectTy; } @@ -2528,7 +2530,7 @@ static Type getNamedProtocolType(ClangImporter::Implementation &impl, impl.importDecl(decl->getUnderlyingDecl(), impl.CurrentVersion)) { if (auto protoDecl = dynCastIgnoringCompatibilityAlias(swiftDecl)) { - return protoDecl->getDeclaredType(); + return protoDecl->getDeclaredInterfaceType(); } } } diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index e87f9f6bf691a..3628481d8a47e 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -776,6 +776,7 @@ NodePointer Demangler::demangleOperator() { case 'V': return demangleAnyGenericType(Node::Kind::Structure); case 'W': return demangleWitness(); case 'X': return demangleSpecialType(); + case 'Y': return createNode(Node::Kind::AsyncAnnotation); case 'Z': return createWithChild(Node::Kind::Static, popNode(isEntity)); case 'a': return demangleAnyGenericType(Node::Kind::TypeAlias); case 'c': return popFunctionType(Node::Kind::FunctionType); @@ -1230,6 +1231,7 @@ NodePointer Demangler::demanglePlainFunction() { NodePointer Demangler::popFunctionType(Node::Kind kind) { NodePointer FuncType = createNode(kind); addChild(FuncType, popNode(Node::Kind::ThrowsAnnotation)); + addChild(FuncType, popNode(Node::Kind::AsyncAnnotation)); FuncType = addChild(FuncType, popFunctionParams(Node::Kind::ArgumentTuple)); FuncType = addChild(FuncType, popFunctionParams(Node::Kind::ReturnType)); @@ -1261,9 +1263,14 @@ NodePointer Demangler::popFunctionParamLabels(NodePointer Type) { FuncType->getKind() != Node::Kind::NoEscapeFunctionType) return nullptr; - auto ParameterType = FuncType->getFirstChild(); - if (ParameterType->getKind() == Node::Kind::ThrowsAnnotation) - ParameterType = FuncType->getChild(1); + unsigned FirstChildIdx = 0; + if (FuncType->getChild(FirstChildIdx)->getKind() + == Node::Kind::ThrowsAnnotation) + ++FirstChildIdx; + if (FuncType->getChild(FirstChildIdx)->getKind() + == Node::Kind::AsyncAnnotation) + ++FirstChildIdx; + auto ParameterType = FuncType->getChild(FirstChildIdx); assert(ParameterType->getKind() == Node::Kind::ArgumentTuple); @@ -1911,9 +1918,14 @@ NodePointer Demangler::demangleMetatype() { case 'j': return createWithChild(Node::Kind::OpaqueTypeDescriptorAccessorKey, popNode()); + case 'J': + return createWithChild(Node::Kind::NoncanonicalSpecializedGenericTypeMetadataCache, popNode()); case 'k': return createWithChild(Node::Kind::OpaqueTypeDescriptorAccessorVar, popNode()); + case 'K': + return createWithChild(Node::Kind::MetadataInstantiationCache, + popNode()); case 'l': return createWithPoppedType( Node::Kind::TypeMetadataSingletonInitializationCache); @@ -1926,6 +1938,9 @@ NodePointer Demangler::demangleMetatype() { Node::Kind::CanonicalSpecializedGenericMetaclass); case 'n': return createWithPoppedType(Node::Kind::NominalTypeDescriptor); + case 'N': + return createWithPoppedType( + Node::Kind::NoncanonicalSpecializedGenericTypeMetadata); case 'o': return createWithPoppedType(Node::Kind::ClassMetadataBaseOffset); case 'p': diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index b6e120722d13c..e7ea0e52e29d6 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -454,6 +454,7 @@ class NodePrinter { case Node::Kind::PropertyDescriptor: case Node::Kind::ProtocolConformance: case Node::Kind::ProtocolConformanceDescriptor: + case Node::Kind::MetadataInstantiationCache: case Node::Kind::ProtocolDescriptor: case Node::Kind::ProtocolRequirementsBaseDescriptor: case Node::Kind::ProtocolSelfConformanceDescriptor: @@ -509,6 +510,7 @@ class NodePrinter { case Node::Kind::ReflectionMetadataSuperclassDescriptor: case Node::Kind::ResilientProtocolWitnessTable: case Node::Kind::GenericTypeParamDecl: + case Node::Kind::AsyncAnnotation: case Node::Kind::ThrowsAnnotation: case Node::Kind::EmptyList: case Node::Kind::FirstElementMarker: @@ -548,6 +550,8 @@ class NodePrinter { case Node::Kind::OpaqueReturnTypeOf: case Node::Kind::CanonicalSpecializedGenericMetaclass: case Node::Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: + case Node::Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Node::Kind::NoncanonicalSpecializedGenericTypeMetadataCache: return false; } printer_unreachable("bad node kind"); @@ -742,13 +746,20 @@ class NodePrinter { } void printFunctionType(NodePointer LabelList, NodePointer node) { - if (node->getNumChildren() != 2 && node->getNumChildren() != 3) { + if (node->getNumChildren() < 2 || node->getNumChildren() > 4) { setInvalid(); return; } unsigned startIndex = 0; - if (node->getChild(0)->getKind() == Node::Kind::ThrowsAnnotation) - startIndex = 1; + bool isAsync = false, isThrows = false; + if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { + ++startIndex; + isThrows = true; + } + if (node->getChild(startIndex)->getKind() == Node::Kind::AsyncAnnotation) { + ++startIndex; + isAsync = true; + } printFunctionParameters(LabelList, node->getChild(startIndex), Options.ShowFunctionArgumentTypes); @@ -756,7 +767,10 @@ class NodePrinter { if (!Options.ShowFunctionArgumentTypes) return; - if (startIndex == 1) + if (isAsync) + Printer << " async"; + + if (isThrows) Printer << " throws"; print(node->getChild(startIndex + 1)); @@ -1128,8 +1142,12 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "):"; } print(Node->getChild(1)); - if (Node->getNumChildren() == 3) - print(Node->getChild(2)); + if (Node->getNumChildren() == 3) { + // Currently the runtime does not mangle the generic signature. + // This is an open to-do in swift::_buildDemanglingForContext(). + if (!Options.PrintForTypeName) + print(Node->getChild(2)); + } return nullptr; case Node::Kind::Variable: return printEntity(Node, asPrefixContext, TypePrinting::WithColon, @@ -2269,6 +2287,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { print(Node->getChild(0)); return nullptr; + case Node::Kind::AsyncAnnotation: + Printer<< " async "; + return nullptr; case Node::Kind::ThrowsAnnotation: Printer<< " throws "; return nullptr; @@ -2433,6 +2454,18 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "canonical specialized generic type metadata accessor for "; print(Node->getChild(0)); return nullptr; + case Node::Kind::MetadataInstantiationCache: + Printer << "metadata instantiation cache for "; + print(Node->getChild(0)); + return nullptr; + case Node::Kind::NoncanonicalSpecializedGenericTypeMetadata: + Printer << "noncanonical specialized generic type metadata for "; + print(Node->getChild(0)); + return nullptr; + case Node::Kind::NoncanonicalSpecializedGenericTypeMetadataCache: + Printer << "cache variable for noncanonical specialized generic type metadata for "; + print(Node->getChild(0)); + return nullptr; } printer_unreachable("bad node kind!"); } diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index e8961e130a8e7..e66d21aaa85f9 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -1765,10 +1765,10 @@ class OldDemangler { } NodePointer demangleFunctionType(Node::Kind kind) { - bool throws = false; - if (Mangled && - Mangled.nextIf('z')) { - throws = true; + bool throws = false, async = false; + if (Mangled) { + throws = Mangled.nextIf('z'); + async = Mangled.nextIf('Z'); } NodePointer in_args = demangleType(); if (!in_args) @@ -1781,7 +1781,10 @@ class OldDemangler { if (throws) { block->addChild(Factory.createNode(Node::Kind::ThrowsAnnotation), Factory); } - + if (async) { + block->addChild(Factory.createNode(Node::Kind::AsyncAnnotation), Factory); + } + NodePointer in_node = Factory.createNode(Node::Kind::ArgumentTuple); block->addChild(in_node, Factory); in_node->addChild(in_args, Factory); @@ -2010,6 +2013,10 @@ class OldDemangler { } } if (c == 'Q') { + if (Mangled.nextIf('u')) { + // Special mangling for opaque return type. + return Factory.createNode(Node::Kind::OpaqueReturnType); + } return demangleArchetypeType(); } if (c == 'q') { diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 67aa6ba5c1e40..ba7ca2eb51960 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -627,6 +627,10 @@ void Remangler::mangleValueWitnessTable(Node *node) { mangleSingleChildNode(node); // type } +void Remangler::mangleAsyncAnnotation(Node *node) { + Buffer << "Z"; +} + void Remangler::mangleThrowsAnnotation(Node *node) { Buffer << "z"; } @@ -2106,7 +2110,7 @@ void Remangler::mangleSugaredParen(Node *node) { } void Remangler::mangleOpaqueReturnType(Node *node) { - unreachable("unsupported"); + Buffer << "Qu"; } void Remangler::mangleOpaqueReturnTypeOf(Node *node, EntityContext &ctx) { unreachable("unsupported"); @@ -2132,6 +2136,9 @@ void Remangler::mangleOpaqueTypeDescriptorAccessorVar(Node *node) { void Remangler::mangleAccessorFunctionReference(Node *node) { unreachable("can't remangle"); } +void Remangler::mangleMetadataInstantiationCache(Node *node) { + unreachable("unsupported"); +} void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node) { Buffer << "MM"; @@ -2144,6 +2151,16 @@ void Remangler::mangleCanonicalSpecializedGenericTypeMetadataAccessFunction( Buffer << "Mb"; } +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata(Node *node) { + mangleSingleChildNode(node); + Buffer << "MN"; +} + +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache(Node *node) { + mangleSingleChildNode(node); + Buffer << "MJ"; +} + /// The top-level interface to the remangler. std::string Demangle::mangleNodeOld(NodePointer node) { if (!node) return ""; diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 1af12dc3c93e5..60dfeafaee925 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2314,6 +2314,10 @@ void Remangler::mangleFullObjCResilientClassStub(Node *node) { Buffer << "Mt"; } +void Remangler::mangleAsyncAnnotation(Node *node) { + Buffer << 'Y'; +} + void Remangler::mangleThrowsAnnotation(Node *node) { Buffer << 'K'; } @@ -2547,6 +2551,21 @@ void Remangler::mangleCanonicalSpecializedGenericTypeMetadataAccessFunction( Buffer << "Mb"; } +void Remangler::mangleMetadataInstantiationCache(Node *node) { + mangleSingleChildNode(node); + Buffer << "MK"; +} + +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata(Node *node) { + mangleSingleChildNode(node); + Buffer << "MN"; +} + +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache(Node *node) { + mangleSingleChildNode(node); + Buffer << "MJ"; +} + } // anonymous namespace /// The top-level interface to the remangler. diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index b4829ab4b402b..cf53235560519 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -2060,10 +2060,7 @@ void Compilation::sortJobsToMatchCompilationInputs( if (const CompileJobAction *CJA = dyn_cast(&J->getSource())) { const InputAction *IA = CJA->findSingleSwiftInput(); - auto R = - jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); - assert(R.second); - (void)R; + jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); } else sortedJobs.push_back(J); } diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index a4e9ecfd3382e..6ec463e2442b0 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -89,12 +89,9 @@ toolchains::Darwin::constructInvocation(const InterpretJobAction &job, } static StringRef -getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple, - bool distinguishSimulator = true) { +getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple) { const DarwinPlatformKind kind = getDarwinPlatformKind(triple); - const DarwinPlatformKind effectiveKind = - distinguishSimulator ? kind : getNonSimulatorPlatform(kind); - switch (effectiveKind) { + switch (kind) { case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: @@ -501,60 +498,11 @@ toolchains::Darwin::addProfileGenerationArgs(ArgStringList &Arguments, } } -/// Remap the given version number via the version map, or produce \c None if -/// there is no mapping for this version. -static Optional remapVersion( - const llvm::StringMap &versionMap, - llvm::VersionTuple version) { - // The build number is never used in the lookup. - version = version.withoutBuild(); - - // Look for this specific version. - auto known = versionMap.find(version.getAsString()); - if (known != versionMap.end()) - return known->second; - - // If an extra ".0" was specified (in the subminor version), drop that - // and look again. - if (!version.getSubminor() || *version.getSubminor() != 0) - return None; - - version = llvm::VersionTuple(version.getMajor(), *version.getMinor()); - known = versionMap.find(version.getAsString()); - if (known != versionMap.end()) - return known->second; - - // If another extra ".0" wa specified (in the minor version), drop that - // and look again. - if (!version.getMinor() || *version.getMinor() != 0) - return None; - - version = llvm::VersionTuple(version.getMajor()); - known = versionMap.find(version.getAsString()); - if (known != versionMap.end()) - return known->second; - - return None; -} - Optional toolchains::Darwin::getTargetSDKVersion(const llvm::Triple &triple) const { if (!SDKInfo) return None; - - // Retrieve the SDK version. - auto SDKVersion = SDKInfo->getVersion(); - - // For the Mac Catalyst environment, we have a macOS SDK with a macOS - // SDK version. Map that to the corresponding iOS version number to pass - // down to the linker. - if (tripleIsMacCatalystEnvironment(triple)) { - return remapVersion( - SDKInfo->getVersionMap().MacOS2iOSMacMapping, SDKVersion) - .getValueOr(llvm::VersionTuple(0, 0, 0)); - } - - return SDKVersion; + return swift::getTargetSDKVersion(*SDKInfo, triple); } void @@ -626,14 +574,6 @@ toolchains::Darwin::addDeploymentTargetArgs(ArgStringList &Arguments, micro = firstMacARM64e.getSubminor().getValueOr(0); } - // Temporary hack: adjust macOS version passed to the linker from - // 11 down to 10.16, but only for x86. - if (triple.isX86() && major == 11) { - major = 10; - minor = 16; - micro = 0; - } - break; case DarwinPlatformKind::IPhoneOS: case DarwinPlatformKind::IPhoneOSSimulator: @@ -758,7 +698,7 @@ toolchains::Darwin::constructInvocation(const DynamicLinkJobAction &job, llvm::sys::path::append( CompilerRTPath, Twine("libclang_rt.") + - getDarwinLibraryNameSuffixForTriple(Triple, /*simulator*/false) + + getDarwinLibraryNameSuffixForTriple(Triple) + ".a"); if (llvm::sys::fs::exists(CompilerRTPath)) Arguments.push_back(context.Args.MakeArgString(CompilerRTPath)); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 516938ae33edc..74640ed9ac387 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -163,6 +163,16 @@ static void validateProfilingArgs(DiagnosticEngine &diags, } } +static void validateDependencyScanningArgs(DiagnosticEngine &diags, + const ArgList &args) { + const Arg *ExternalDependencyMap = args.getLastArg(options::OPT_placeholder_dependency_module_map); + const Arg *ScanDependencies = args.getLastArg(options::OPT_scan_dependencies); + if (ExternalDependencyMap && !ScanDependencies) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-placeholder-dependency-module-map-file", "-scan-dependencies"); + } +} + static void validateDebugInfoArgs(DiagnosticEngine &diags, const ArgList &args) { // Check for missing debug option when verifying debug info. @@ -261,6 +271,7 @@ static void validateArgs(DiagnosticEngine &diags, const ArgList &args, validateBridgingHeaderArgs(diags, args); validateWarningControlArgs(diags, args); validateProfilingArgs(diags, args); + validateDependencyScanningArgs(diags, args); validateDebugInfoArgs(diags, args); validateCompilationConditionArgs(diags, args); validateSearchPathArgs(diags, args); @@ -2241,6 +2252,13 @@ bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) { commandLine.push_back(resourceDirArg->getValue()); } + if (Args.hasFlag(options::OPT_static_executable, + options::OPT_no_static_executable, false) || + Args.hasFlag(options::OPT_static_stdlib, options::OPT_no_static_stdlib, + false)) { + commandLine.push_back("-use-static-resource-dir"); + } + std::string executable = getSwiftProgramPath(); // FIXME: This bypasses mechanisms like -v and -###. (SR-12119) diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index bc1cda3e359f7..8725924d68b27 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -196,6 +196,8 @@ std::vector ModuleDepGraph::getExternalDependencies() const { } // Add every (swiftdeps) use of the external dependency to foundJobs. +// Can return duplicates, but it doesn't break anything, and they will be +// canonicalized later. std::vector ModuleDepGraph::findExternallyDependentUntracedJobs( StringRef externalDependency) { FrontendStatsTracer tracer( diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 8e7f465d04371..d92cb1c568bc4 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -22,6 +22,7 @@ #include "swift/Driver/Compilation.h" #include "swift/Driver/Driver.h" #include "swift/Driver/Job.h" +#include "swift/Frontend/Frontend.h" #include "swift/Option/Options.h" #include "clang/Basic/Version.h" #include "clang/Driver/Util.h" @@ -264,6 +265,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup); inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_concise_pound_file); + inputArgs.AddLastArg( + arguments, + options::OPT_enable_fuzzy_forward_scan_trailing_closure_matching, + options::OPT_disable_fuzzy_forward_scan_trailing_closure_matching); inputArgs.AddLastArg(arguments, options::OPT_verify_incremental_dependencies); inputArgs.AddLastArg(arguments, @@ -533,6 +538,13 @@ ToolChain::constructInvocation(const CompileJobAction &job, Arguments.push_back("-track-system-dependencies"); } + if (context.Args.hasFlag(options::OPT_static_executable, + options::OPT_no_static_executable, false) || + context.Args.hasFlag(options::OPT_static_stdlib, + options::OPT_no_static_stdlib, false)) { + Arguments.push_back("-use-static-resource-dir"); + } + context.Args.AddLastArg( Arguments, options:: @@ -1266,9 +1278,6 @@ void ToolChain::getClangLibraryPath(const ArgList &Args, void ToolChain::getResourceDirPath(SmallVectorImpl &resourceDirPath, const llvm::opt::ArgList &args, bool shared) const { - // FIXME: Duplicated from CompilerInvocation, but in theory the runtime - // library link path and the standard library module import path don't - // need to be the same. if (const Arg *A = args.getLastArg(options::OPT_resource_dir)) { StringRef value = A->getValue(); resourceDirPath.append(value.begin(), value.end()); @@ -1276,15 +1285,12 @@ void ToolChain::getResourceDirPath(SmallVectorImpl &resourceDirPath, // for WASI, sdk option points to wasi-sysroot which doesn't have Swift toolchain StringRef value = args.getLastArg(options::OPT_sdk)->getValue(); resourceDirPath.append(value.begin(), value.end()); - llvm::sys::path::append(resourceDirPath, "usr", "lib", - shared ? "swift" : "swift_static"); + llvm::sys::path::append(resourceDirPath, "usr"); + CompilerInvocation::appendSwiftLibDir(resourceDirPath, shared); } else { auto programPath = getDriver().getSwiftProgramPath(); - resourceDirPath.append(programPath.begin(), programPath.end()); - llvm::sys::path::remove_filename(resourceDirPath); // remove /swift - llvm::sys::path::remove_filename(resourceDirPath); // remove /bin - llvm::sys::path::append(resourceDirPath, "lib", - shared ? "swift" : "swift_static"); + CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( + programPath, shared, resourceDirPath); } StringRef libSubDir = getPlatformNameForTriple(getTriple()); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 812890d7aec58..05b0ed04f5083 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -193,6 +193,7 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.EnableSourceImport |= Args.hasArg(OPT_enable_source_import); Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module); Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies); + Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir); computeImportObjCHeaderOptions(); computeImplicitImportModuleNames(); @@ -234,8 +235,6 @@ void ArgsToFrontendOptionsConverter::computePrintStatsOptions() { void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() { using namespace options; - Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation); - if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) { Opts.StatsOutputDir = A->getValue(); if (Args.getLastArg(OPT_trace_stats_events)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 336e22511e068..32ba70f163950 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -44,16 +44,26 @@ swift::CompilerInvocation::CompilerInvocation() { } void CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( - StringRef mainExecutablePath, llvm::SmallString<128> &runtimeResourcePath) { - runtimeResourcePath.assign(mainExecutablePath); + StringRef mainExecutablePath, bool shared, + llvm::SmallVectorImpl &runtimeResourcePath) { + runtimeResourcePath.append(mainExecutablePath.begin(), + mainExecutablePath.end()); + llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /swift llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /bin - llvm::sys::path::append(runtimeResourcePath, "lib", "swift"); + appendSwiftLibDir(runtimeResourcePath, shared); +} + +void CompilerInvocation::appendSwiftLibDir(llvm::SmallVectorImpl &path, + bool shared) { + llvm::sys::path::append(path, "lib", shared ? "swift" : "swift_static"); } void CompilerInvocation::setMainExecutablePath(StringRef Path) { + FrontendOpts.MainExecutablePath = Path.str(); llvm::SmallString<128> LibPath; - computeRuntimeResourcePathFromExecutablePath(Path, LibPath); + computeRuntimeResourcePathFromExecutablePath( + Path, FrontendOpts.UseSharedResourceFolder, LibPath); setRuntimeResourcePath(LibPath.str()); llvm::SmallString<128> DiagnosticDocsPath(Path); @@ -63,12 +73,11 @@ void CompilerInvocation::setMainExecutablePath(StringRef Path) { "diagnostics"); DiagnosticOpts.DiagnosticDocumentationPath = std::string(DiagnosticDocsPath.str()); - // Compute the path of the YAML diagnostic messages directory files - // in the toolchain. + // Compute the path to the diagnostic translations in the toolchain/build. llvm::SmallString<128> DiagnosticMessagesDir(Path); llvm::sys::path::remove_filename(DiagnosticMessagesDir); // Remove /swift llvm::sys::path::remove_filename(DiagnosticMessagesDir); // Remove /bin - llvm::sys::path::append(DiagnosticMessagesDir, "share", "swift"); + llvm::sys::path::append(DiagnosticMessagesDir, "share", "swift", "diagnostics"); DiagnosticOpts.LocalizationPath = std::string(DiagnosticMessagesDir.str()); } @@ -89,6 +98,20 @@ void CompilerInvocation::setDefaultPrebuiltCacheIfNecessary() { platform = getPlatformNameForTriple(LangOpts.Target); } llvm::sys::path::append(defaultPrebuiltPath, platform, "prebuilt-modules"); + + // If the SDK version is given, we should check if SDK-versioned prebuilt + // module cache is available and use it if so. + if (auto ver = LangOpts.SDKVersion) { + // "../macosx/prebuilt-modules" + SmallString<64> defaultPrebuiltPathWithSDKVer = defaultPrebuiltPath; + // "../macosx/prebuilt-modules/10.15" + llvm::sys::path::append(defaultPrebuiltPathWithSDKVer, ver->getAsString()); + // If the versioned prebuilt module cache exists in the disk, use it. + if (llvm::sys::fs::exists(defaultPrebuiltPathWithSDKVer)) { + FrontendOpts.PrebuiltModuleCachePath = defaultPrebuiltPathWithSDKVer.str(); + return; + } + } FrontendOpts.PrebuiltModuleCachePath = defaultPrebuiltPath.str(); } @@ -300,6 +323,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Args.hasArg(OPT_module_interface_preserve_types_as_written); Opts.PrintFullConvention |= Args.hasArg(OPT_experimental_print_full_convention); + Opts.ExperimentalSPIImports |= + Args.hasArg(OPT_experimental_spi_imports); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running @@ -360,6 +385,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableExperimentalStaticAssert |= Args.hasArg(OPT_enable_experimental_static_assert); + Opts.EnableExperimentalConcurrency |= + Args.hasArg(OPT_enable_experimental_concurrency); + Opts.EnableSubstSILFunctionTypesForFunctionValues |= Args.hasArg(OPT_enable_subst_sil_function_types_for_function_values); @@ -559,6 +587,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableConcisePoundFile = Args.hasArg(OPT_enable_experimental_concise_pound_file); + Opts.EnableFuzzyForwardScanTrailingClosureMatching = + Args.hasFlag(OPT_enable_fuzzy_forward_scan_trailing_closure_matching, + OPT_disable_fuzzy_forward_scan_trailing_closure_matching, + true); Opts.EnableCrossImportOverlays = Args.hasFlag(OPT_enable_cross_import_overlays, @@ -586,6 +618,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.VerifyAllSubstitutionMaps |= Args.hasArg(OPT_verify_all_substitution_maps); + Opts.EnableVolatileModules |= Args.hasArg(OPT_enable_volatile_modules); + Opts.UseDarwinPreStableABIBit = (Target.isMacOSX() && Target.isMacOSXVersionLT(10, 14, 4)) || (Target.isiOS() && Target.isOSVersionLT(12, 2)) || @@ -874,6 +908,12 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, } if (const Arg *A = Args.getLastArg(OPT_explict_swift_module_map)) Opts.ExplicitSwiftModuleMap = A->getValue(); + for (auto A: Args.filtered(OPT_candidate_module_file)) { + Opts.CandidateCompiledModules.push_back(resolveSearchPath(A->getValue())); + } + if (const Arg *A = Args.getLastArg(OPT_placeholder_dependency_module_map)) + Opts.PlaceholderDependencyModuleMap = A->getValue(); + // Opts.RuntimeIncludePath is set by calls to // setRuntimeIncludePath() or setMainExecutablePath(). // Opts.RuntimeImportPath is set by calls to @@ -1395,9 +1435,23 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_disable_concrete_type_metadata_mangled_name_accessors)) Opts.DisableConcreteTypeMetadataMangledNameAccessors = true; - if (Args.hasArg(OPT_use_jit)) + if (Args.hasArg(OPT_use_jit)) { Opts.UseJIT = true; - + if (const Arg *A = Args.getLastArg(OPT_dump_jit)) { + llvm::Optional artifact = + llvm::StringSwitch>(A->getValue()) + .Case("llvm-ir", JITDebugArtifact::LLVMIR) + .Case("object", JITDebugArtifact::Object) + .Default(None); + if (!artifact) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getOption().getName(), A->getValue()); + return true; + } + Opts.DumpJIT = *artifact; + } + } + for (const Arg *A : Args.filtered(OPT_verify_type_layout)) { Opts.VerifyTypeLayoutNames.push_back(A->getValue()); } @@ -1683,11 +1737,10 @@ static bool ParseMigratorArgs(MigratorOptions &Opts, } bool CompilerInvocation::parseArgs( - ArrayRef Args, - DiagnosticEngine &Diags, + ArrayRef Args, DiagnosticEngine &Diags, SmallVectorImpl> *ConfigurationFileBuffers, - StringRef workingDirectory) { + StringRef workingDirectory, StringRef mainExecutablePath) { using namespace options; if (Args.empty()) @@ -1718,6 +1771,10 @@ bool CompilerInvocation::parseArgs( return true; } + if (!mainExecutablePath.empty()) { + setMainExecutablePath(mainExecutablePath); + } + ParseModuleInterfaceArgs(ModuleInterfaceOpts, ParsedArgs); SaveModuleInterfaceArgs(ModuleInterfaceOpts, FrontendOpts, ParsedArgs, Diags); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 9aff435557268..7e000a0b6a563 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -424,14 +424,17 @@ void CompilerInstance::setUpDiagnosticOptions() { // Ideally, we'd get rid of it. // 2. MemoryBufferSerializedModuleLoader: This is used by LLDB, because it might // already have the module available in memory. -// 3. ModuleInterfaceLoader: Tries to find an up-to-date swiftmodule. If it +// 3. ExplicitSwiftModuleLoader: Loads a serialized module if it can, provided +// this modules was specified as an explicit input to the compiler. +// 4. ModuleInterfaceLoader: Tries to find an up-to-date swiftmodule. If it // succeeds, it issues a particular "error" (see -// [Note: ModuleInterfaceLoader-defer-to-SerializedModuleLoader]), which -// is interpreted by the overarching loader as a command to use the -// SerializedModuleLoader. If we failed to find a .swiftmodule, this falls -// back to using an interface. Actual errors lead to diagnostics. -// 4. SerializedModuleLoader: Loads a serialized module if it can. -// 5. ClangImporter: This must come after all the Swift module loaders because +// [NOTE: ModuleInterfaceLoader-defer-to-ImplicitSerializedModuleLoader]), +// which is interpreted by the overarching loader as a command to use the +// ImplicitSerializedModuleLoader. If we failed to find a .swiftmodule, +// this falls back to using an interface. Actual errors lead to diagnostics. +// 5. ImplicitSerializedModuleLoader: Loads a serialized module if it can. +// Used for implicit loading of modules from the compiler's search paths. +// 6. ClangImporter: This must come after all the Swift module loaders because // in the presence of overlays and mixed-source frameworks, we want to prefer // the overlay or framework module over the underlying Clang module. bool CompilerInstance::setUpModuleLoaders() { @@ -484,15 +487,18 @@ bool CompilerInstance::setUpModuleLoaders() { // If implicit modules are disabled, we need to install an explicit module // loader. - if (Invocation.getFrontendOptions().DisableImplicitModules) { + bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; + if (ExplicitModuleBuild) { auto ESML = ExplicitSwiftModuleLoader::create( *Context, getDependencyTracker(), MLM, Invocation.getSearchPathOptions().ExplicitSwiftModules, Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, IgnoreSourceInfoFile); + this->DefaultSerializedLoader = ESML.get(); Context->addModuleLoader(std::move(ESML)); } + if (MLM != ModuleLoadingMode::OnlySerialized) { auto const &Clang = clangImporter->getClangInstance(); std::string ModuleCachePath = getModuleCachePathFromClang(Clang); @@ -507,12 +513,14 @@ bool CompilerInstance::setUpModuleLoaders() { Context->addModuleLoader(std::move(PIML), false, false, true); } - std::unique_ptr SML = - SerializedModuleLoader::create(*Context, getDependencyTracker(), MLM, + if (!ExplicitModuleBuild) { + std::unique_ptr ISML = + ImplicitSerializedModuleLoader::create(*Context, getDependencyTracker(), MLM, IgnoreSourceInfoFile); - this->SML = SML.get(); - Context->addModuleLoader(std::move(SML)); - + this->DefaultSerializedLoader = ISML.get(); + Context->addModuleLoader(std::move(ISML)); + } + Context->addModuleLoader(std::move(clangImporter), /*isClang*/ true); return false; @@ -868,6 +876,7 @@ bool CompilerInstance::loadStdlibIfNeeded() { bool CompilerInstance::loadPartialModulesAndImplicitImports( ModuleDecl *mod, SmallVectorImpl &partialModules) const { + assert(DefaultSerializedLoader && "Expected module loader in Compiler Instance"); FrontendStatsTracer tracer(getStatsReporter(), "load-partial-modules-and-implicit-imports"); // Force loading implicit imports. This is currently needed to allow @@ -881,7 +890,7 @@ bool CompilerInstance::loadPartialModulesAndImplicitImports( for (auto &PM : PartialModules) { assert(PM.ModuleBuffer); auto *file = - SML->loadAST(*mod, /*diagLoc*/ SourceLoc(), /*moduleInterfacePath*/ "", + DefaultSerializedLoader->loadAST(*mod, /*diagLoc*/ SourceLoc(), /*moduleInterfacePath*/ "", std::move(PM.ModuleBuffer), std::move(PM.ModuleDocBuffer), std::move(PM.ModuleSourceInfoBuffer), /*isFramework*/ false); @@ -974,7 +983,7 @@ void CompilerInstance::freeASTContext() { TheSILTypes.reset(); Context.reset(); MainModule = nullptr; - SML = nullptr; + DefaultSerializedLoader = nullptr; MemoryBufferLoader = nullptr; PrimaryBufferIDs.clear(); } diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index bcfb5c6dd5968..4e118f04c0e5a 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -147,7 +147,8 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer) { + std::unique_ptr *ModuleBuffer, + ArrayRef CompiledCandidates) { auto outerPrettyStackState = llvm::SavePrettyStackState(); @@ -167,6 +168,13 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( [&](SubCompilerInstanceInfo &info) { auto &SubInstance = *info.Instance; auto subInvocation = SubInstance.getInvocation(); + // Try building forwarding module first. If succeed, return. + if (static_cast(SubInstance.getASTContext() + .getModuleInterfaceLoader())->tryEmitForwardingModule(moduleName, + interfacePath, + CompiledCandidates, OutPath)) { + return false; + } FrontendOptions &FEOpts = subInvocation.getFrontendOptions(); const auto &InputInfo = FEOpts.InputsAndOutputs.firstInput(); StringRef InPath = InputInfo.file(); @@ -256,12 +264,14 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild) { + llvm::function_ref RemarkRebuild, + ArrayRef CompiledCandidates) { auto build = [&]() { if (RemarkRebuild) { RemarkRebuild(); } - return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer, + CompiledCandidates); }; if (disableInterfaceFileLock) { return build(); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index e07fa18f08b1b..ae90b2ded60f9 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -85,7 +85,8 @@ class ModuleInterfaceBuilder { bool IsHashBased); bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + ArrayRef CandidateModules); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, InterfaceSubContextDelegate &subASTDelegate, @@ -111,7 +112,8 @@ class ModuleInterfaceBuilder { bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild = nullptr); + llvm::function_ref RemarkRebuild = nullptr, + ArrayRef CandidateModules = {}); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 42ca32c8cd682..fed69f9a43f13 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -590,12 +590,8 @@ class ModuleInterfaceLoaderImpl { return path.startswith(resourceDir); } - llvm::ErrorOr - discoverUpToDateCompiledModuleForInterface(SmallVectorImpl &deps, - std::string &UsableModulePath) { - auto notFoundError = - std::make_error_code(std::errc::no_such_file_or_directory); - + std::pair getCompiledModuleCandidates() { + std::pair result; // Keep track of whether we should attempt to load a .swiftmodule adjacent // to the .swiftinterface. bool shouldLoadAdjacentModule = true; @@ -604,7 +600,7 @@ class ModuleInterfaceLoaderImpl { case ModuleLoadingMode::OnlyInterface: // Always skip both the caches and adjacent modules, and always build the // module interface. - return notFoundError; + return {}; case ModuleLoadingMode::PreferInterface: // If we're in the load mode that prefers .swiftinterfaces, specifically // skip the module adjacent to the interface, but use the caches if @@ -617,7 +613,7 @@ class ModuleInterfaceLoaderImpl { case ModuleLoadingMode::OnlySerialized: llvm_unreachable("module interface loader should not have been created"); } - // [Note: ModuleInterfaceLoader-defer-to-SerializedModuleLoader] + // [NOTE: ModuleInterfaceLoader-defer-to-ImplicitSerializedModuleLoader] // If there's a module adjacent to the .swiftinterface that we can // _likely_ load (it validates OK and is up to date), bail early with // errc::not_supported, so the next (serialized) loader in the chain will @@ -627,16 +623,48 @@ class ModuleInterfaceLoaderImpl { // diagnose it. if (shouldLoadAdjacentModule) { - auto adjacentModuleBuffer = fs.getBufferForFile(modulePath); + if (fs.exists(modulePath)) { + result.first = modulePath; + } + } + + // If we have a prebuilt cache path, check that too if the interface comes + // from the SDK. + if (!prebuiltCacheDir.empty()) { + llvm::SmallString<256> scratch; + std::unique_ptr moduleBuffer; + Optional path = computePrebuiltModulePath(scratch); + if (!path) { + // Hack: deal with prebuilds of modules that still use the target-based + // names. + path = computeFallbackPrebuiltModulePath(scratch); + } + if (path) { + if (fs.exists(*path)) { + result.second = *path; + } + } + } + + return result; + } + + llvm::ErrorOr + discoverUpToDateCompiledModuleForInterface(SmallVectorImpl &deps, + std::string &UsableModulePath) { + std::string adjacentMod, prebuiltMod; + std::tie(adjacentMod, prebuiltMod) = getCompiledModuleCandidates(); + if (!adjacentMod.empty()) { + auto adjacentModuleBuffer = fs.getBufferForFile(adjacentMod); if (adjacentModuleBuffer) { - if (serializedASTBufferIsUpToDate(modulePath, *adjacentModuleBuffer.get(), + if (serializedASTBufferIsUpToDate(adjacentMod, *adjacentModuleBuffer.get(), deps)) { LLVM_DEBUG(llvm::dbgs() << "Found up-to-date module at " - << modulePath + << adjacentMod << "; deferring to serialized module loader\n"); - UsableModulePath = modulePath.str(); + UsableModulePath = adjacentMod; return std::make_error_code(std::errc::not_supported); - } else if (isInResourceDir(modulePath) && + } else if (isInResourceDir(adjacentMod) && loadMode == ModuleLoadingMode::PreferSerialized) { // Special-case here: If we're loading a .swiftmodule from the resource // dir adjacent to the compiler, defer to the serialized loader instead @@ -647,53 +675,40 @@ class ModuleInterfaceLoaderImpl { // we used to. LLVM_DEBUG(llvm::dbgs() << "Found out-of-date module in the " "resource-dir at " - << modulePath + << adjacentMod << "; deferring to serialized module loader " "to diagnose\n"); return std::make_error_code(std::errc::not_supported); } else { LLVM_DEBUG(llvm::dbgs() << "Found out-of-date module at " - << modulePath << "\n"); - rebuildInfo.setModuleKind(modulePath, + << adjacentMod << "\n"); + rebuildInfo.setModuleKind(adjacentMod, ModuleRebuildInfo::ModuleKind::Normal); } - } else if (adjacentModuleBuffer.getError() != notFoundError) { + } else { LLVM_DEBUG(llvm::dbgs() << "Found unreadable module at " - << modulePath + << adjacentMod << "; deferring to serialized module loader\n"); return std::make_error_code(std::errc::not_supported); } } - // If we have a prebuilt cache path, check that too if the interface comes - // from the SDK. - if (!prebuiltCacheDir.empty()) { - llvm::SmallString<256> scratch; + if(!prebuiltMod.empty()) { std::unique_ptr moduleBuffer; - Optional path = computePrebuiltModulePath(scratch); - if (!path) { - // Hack: deal with prebuilds of modules that still use the target-based - // names. - path = computeFallbackPrebuiltModulePath(scratch); - } - if (path) { - if (swiftModuleIsUpToDate(*path, deps, moduleBuffer)) { - LLVM_DEBUG(llvm::dbgs() << "Found up-to-date prebuilt module at " - << path->str() << "\n"); - UsableModulePath = path->str(); - return DiscoveredModule::prebuilt(*path, std::move(moduleBuffer)); - } else { - LLVM_DEBUG(llvm::dbgs() << "Found out-of-date prebuilt module at " - << path->str() << "\n"); - rebuildInfo.setModuleKind(*path, - ModuleRebuildInfo::ModuleKind::Prebuilt); - } + if (swiftModuleIsUpToDate(prebuiltMod, deps, moduleBuffer)) { + LLVM_DEBUG(llvm::dbgs() << "Found up-to-date prebuilt module at " + << prebuiltMod << "\n"); + UsableModulePath = prebuiltMod; + return DiscoveredModule::prebuilt(prebuiltMod, std::move(moduleBuffer)); + } else { + LLVM_DEBUG(llvm::dbgs() << "Found out-of-date prebuilt module at " + << prebuiltMod << "\n"); + rebuildInfo.setModuleKind(prebuiltMod, + ModuleRebuildInfo::ModuleKind::Prebuilt); } } - - // Couldn't find an up-to-date .swiftmodule, will need to build module from - // interface. - return notFoundError; + // We cannot find any proper compiled module to use. + return std::make_error_code(std::errc::no_such_file_or_directory); } /// Finds the most appropriate .swiftmodule, whose dependencies are up to @@ -837,7 +852,7 @@ class ModuleInterfaceLoaderImpl { /*serializeDependencyHashes*/false, trackSystemDependencies); // Set up a builder if we need to build the module. It'll also set up - // the subinvocation we'll need to use to compute the cache paths. + // the genericSubInvocation we'll need to use to compute the cache paths. ModuleInterfaceBuilder builder( ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, prebuiltCacheDir, @@ -1000,9 +1015,9 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( return std::error_code(); } -std::string -ModuleInterfaceLoader::getUpToDateCompiledModuleForInterface(StringRef moduleName, - StringRef interfacePath) { +std::vector +ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) { // Derive .swiftmodule path from the .swiftinterface path. auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); llvm::SmallString<32> modulePath = interfacePath; @@ -1014,10 +1029,49 @@ ModuleInterfaceLoader::getUpToDateCompiledModuleForInterface(StringRef moduleNam dependencyTracker, llvm::is_contained(PreferInterfaceForModules, moduleName) ? ModuleLoadingMode::PreferInterface : LoadMode); - SmallVector allDeps; - std::string usableModulePath; - Impl.discoverUpToDateCompiledModuleForInterface(allDeps, usableModulePath); - return usableModulePath; + std::vector results; + auto pair = Impl.getCompiledModuleCandidates(); + // Add compiled module candidates only when they are non-empty. + if (!pair.first.empty()) + results.push_back(pair.first); + if (!pair.second.empty()) + results.push_back(pair.second); + return results; +} + +bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outputPath) { + // Derive .swiftmodule path from the .swiftinterface path. + auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); + llvm::SmallString<32> modulePath = interfacePath; + llvm::sys::path::replace_extension(modulePath, newExt); + ModuleInterfaceLoaderImpl Impl( + Ctx, modulePath, interfacePath, moduleName, + CacheDir, PrebuiltCacheDir, SourceLoc(), + Opts, + dependencyTracker, + llvm::is_contained(PreferInterfaceForModules, moduleName) ? + ModuleLoadingMode::PreferInterface : LoadMode); + SmallVector deps; + std::unique_ptr moduleBuffer; + for (auto mod: candidates) { + // Check if the candidate compiled module is still up-to-date. + if (Impl.swiftModuleIsUpToDate(mod, deps, moduleBuffer)) { + // If so, emit a forwarding module to the candidate. + ForwardingModule FM(mod); + auto hadError = withOutputFile(Ctx.Diags, outputPath, + [&](llvm::raw_pwrite_stream &out) { + llvm::yaml::Output yamlWriter(out); + yamlWriter << FM; + return false; + }); + if (!hadError) + return true; + } + } + return false; } bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( @@ -1049,7 +1103,8 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true, - /*ModuleBuffer*/nullptr); + /*ModuleBuffer*/nullptr, nullptr, + SearchPathOpts.CandidateCompiledModules); } void ModuleInterfaceLoader::collectVisibleTopLevelModuleNames( @@ -1063,68 +1118,55 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface( const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts) { GenericArgs.push_back("-frontend"); - // Start with a SubInvocation that copies various state from our + // Start with a genericSubInvocation that copies various state from our // invoking ASTContext. GenericArgs.push_back("-compile-module-from-interface"); - subInvocation.setTargetTriple(LangOpts.Target); + genericSubInvocation.setTargetTriple(LangOpts.Target); - auto triple = ArgSaver.save(subInvocation.getTargetTriple()); + auto triple = ArgSaver.save(genericSubInvocation.getTargetTriple()); if (!triple.empty()) { GenericArgs.push_back("-target"); GenericArgs.push_back(triple); } // Inherit the Swift language version - subInvocation.getLangOptions().EffectiveLanguageVersion = + genericSubInvocation.getLangOptions().EffectiveLanguageVersion = LangOpts.EffectiveLanguageVersion; GenericArgs.push_back("-swift-version"); - GenericArgs.push_back(ArgSaver.save(subInvocation.getLangOptions() + GenericArgs.push_back(ArgSaver.save(genericSubInvocation.getLangOptions() .EffectiveLanguageVersion.asAPINotesVersionString())); - subInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths); - llvm::for_each(SearchPathOpts.ImportSearchPaths, - [&](const std::string &path) { - GenericArgs.push_back("-I"); - GenericArgs.push_back(path); - }); - subInvocation.setFrameworkSearchPaths(SearchPathOpts.FrameworkSearchPaths); - llvm::for_each(SearchPathOpts.FrameworkSearchPaths, - [&](const SearchPathOptions::FrameworkSearchPath &path) { - GenericArgs.push_back(path.IsSystem? "-Fsystem": "-F"); - GenericArgs.push_back(path.Path); - }); + genericSubInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths); + genericSubInvocation.setFrameworkSearchPaths(SearchPathOpts.FrameworkSearchPaths); if (!SearchPathOpts.SDKPath.empty()) { - subInvocation.setSDKPath(SearchPathOpts.SDKPath); - GenericArgs.push_back("-sdk"); - GenericArgs.push_back(SearchPathOpts.SDKPath); + genericSubInvocation.setSDKPath(SearchPathOpts.SDKPath); } - subInvocation.setInputKind(InputFileKind::SwiftModuleInterface); + genericSubInvocation.setInputKind(InputFileKind::SwiftModuleInterface); if (!SearchPathOpts.RuntimeResourcePath.empty()) { - subInvocation.setRuntimeResourcePath(SearchPathOpts.RuntimeResourcePath); - GenericArgs.push_back("-resource-dir"); - GenericArgs.push_back(SearchPathOpts.RuntimeResourcePath); + genericSubInvocation.setRuntimeResourcePath(SearchPathOpts.RuntimeResourcePath); } - // Inhibit warnings from the SubInvocation since we are assuming the user + // Inhibit warnings from the genericSubInvocation since we are assuming the user // is not in a position to fix them. - subInvocation.getDiagnosticOptions().SuppressWarnings = true; + genericSubInvocation.getDiagnosticOptions().SuppressWarnings = true; GenericArgs.push_back("-suppress-warnings"); // Inherit this setting down so that it can affect error diagnostics (mostly // by making them non-fatal). - subInvocation.getLangOptions().DebuggerSupport = LangOpts.DebuggerSupport; + genericSubInvocation.getLangOptions().DebuggerSupport = LangOpts.DebuggerSupport; if (LangOpts.DebuggerSupport) { GenericArgs.push_back("-debugger-support"); } // Disable this; deinitializers always get printed with `@objc` even in // modules that don't import Foundation. - subInvocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; + genericSubInvocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; GenericArgs.push_back("-disable-objc-attr-requires-foundation-module"); } bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( + CompilerInvocation &subInvocation, SmallVectorImpl &SubArgs, std::string &CompilerVersion, StringRef interfacePath, @@ -1196,7 +1238,7 @@ bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( } void InterfaceSubContextDelegateImpl::addExtraClangArg(StringRef arg) { - subInvocation.getClangImporterOptions().ExtraArgs.push_back(arg); + genericSubInvocation.getClangImporterOptions().ExtraArgs.push_back(arg); GenericArgs.push_back("-Xcc"); GenericArgs.push_back(ArgSaver.save(arg)); } @@ -1213,52 +1255,38 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( StringRef prebuiltCachePath, bool serializeDependencyHashes, bool trackSystemDependencies): SM(SM), Diags(Diags), ArgSaver(Allocator) { + genericSubInvocation.setMainExecutablePath(LoaderOpts.mainExecutablePath); inheritOptionsForBuildingInterface(searchPathOpts, langOpts); // Configure front-end input. - auto &SubFEOpts = subInvocation.getFrontendOptions(); + auto &SubFEOpts = genericSubInvocation.getFrontendOptions(); SubFEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly; if (!moduleCachePath.empty()) { - subInvocation.setClangModuleCachePath(moduleCachePath); - GenericArgs.push_back("-module-cache-path"); - GenericArgs.push_back(moduleCachePath); + genericSubInvocation.setClangModuleCachePath(moduleCachePath); } if (!prebuiltCachePath.empty()) { - subInvocation.getFrontendOptions().PrebuiltModuleCachePath = - prebuiltCachePath.str(); - GenericArgs.push_back("-prebuilt-module-cache-path"); - GenericArgs.push_back(prebuiltCachePath); + genericSubInvocation.getFrontendOptions().PrebuiltModuleCachePath = + prebuiltCachePath.str(); } if (trackSystemDependencies) { - subInvocation.getFrontendOptions().IntermoduleDependencyTracking = + genericSubInvocation.getFrontendOptions().IntermoduleDependencyTracking = IntermoduleDepTrackingMode::IncludeSystem; GenericArgs.push_back("-track-system-dependencies"); } else { // Always track at least the non-system dependencies for interface building. - subInvocation.getFrontendOptions().IntermoduleDependencyTracking = + genericSubInvocation.getFrontendOptions().IntermoduleDependencyTracking = IntermoduleDepTrackingMode::ExcludeSystem; } if (LoaderOpts.disableImplicitSwiftModule) { - subInvocation.getFrontendOptions().DisableImplicitModules = true; + genericSubInvocation.getFrontendOptions().DisableImplicitModules = true; GenericArgs.push_back("-disable-implicit-swift-modules"); } - subInvocation.getSearchPathOptions().ExplicitSwiftModules = + genericSubInvocation.getSearchPathOptions().ExplicitSwiftModules = searchPathOpts.ExplicitSwiftModules; - // Dependencies scanner shouldn't know any explict Swift modules to use. - // Adding these argumnets may not be necessary. - // FIXME: remove it? - for (auto EM: searchPathOpts.ExplicitSwiftModules) { - GenericArgs.push_back("-swift-module-file"); - GenericArgs.push_back(ArgSaver.save(EM)); - } // Pass down -explicit-swift-module-map-file // FIXME: we shouldn't need this. Remove it? StringRef explictSwiftModuleMap = searchPathOpts.ExplicitSwiftModuleMap; - subInvocation.getSearchPathOptions().ExplicitSwiftModuleMap = + genericSubInvocation.getSearchPathOptions().ExplicitSwiftModuleMap = explictSwiftModuleMap; - if (!explictSwiftModuleMap.empty()) { - GenericArgs.push_back("-explicit-swift-module-map-file"); - GenericArgs.push_back(explictSwiftModuleMap); - } if (clangImporter) { // We need to add these extra clang flags because explict module building // related flags are all there: -fno-implicit-modules, -fmodule-map-file=, @@ -1273,19 +1301,19 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( // required by sourcekitd. auto &Opts = clangImporter->getClangInstance().getPreprocessorOpts(); if (Opts.DetailedRecord) { - subInvocation.getClangImporterOptions().DetailedPreprocessingRecord = true; + genericSubInvocation.getClangImporterOptions().DetailedPreprocessingRecord = true; } } - // Tell the subinvocation to serialize dependency hashes if asked to do so. - auto &frontendOpts = subInvocation.getFrontendOptions(); + // Tell the genericSubInvocation to serialize dependency hashes if asked to do so. + auto &frontendOpts = genericSubInvocation.getFrontendOptions(); frontendOpts.SerializeModuleInterfaceDependencyHashes = serializeDependencyHashes; if (serializeDependencyHashes) { GenericArgs.push_back("-serialize-module-interface-dependency-hashes"); } - // Tell the subinvocation to remark on rebuilds from an interface if asked + // Tell the genericSubInvocation to remark on rebuilds from an interface if asked // to do so. frontendOpts.RemarkOnRebuildFromModuleInterface = LoaderOpts.remarkOnRebuildFromInterface; @@ -1299,14 +1327,14 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( (void)llvm::sys::fs::create_directories(moduleCachePath); } -/// Calculate an output filename in \p SubInvocation's cache path that +/// Calculate an output filename in \p genericSubInvocation's cache path that /// includes a hash of relevant key data. StringRef InterfaceSubContextDelegateImpl::computeCachedOutputPath( StringRef moduleName, StringRef useInterfacePath, llvm::SmallString<256> &OutPath, StringRef &CacheHash) { - OutPath = subInvocation.getClangModuleCachePath(); + OutPath = genericSubInvocation.getClangModuleCachePath(); llvm::sys::path::append(OutPath, moduleName); OutPath.append("-"); auto hashStart = OutPath.size(); @@ -1331,7 +1359,7 @@ StringRef InterfaceSubContextDelegateImpl::computeCachedOutputPath( std::string InterfaceSubContextDelegateImpl::getCacheHash(StringRef useInterfacePath) { auto normalizedTargetTriple = - getTargetSpecificModuleTriple(subInvocation.getLangOptions().Target); + getTargetSpecificModuleTriple(genericSubInvocation.getLangOptions().Target); llvm::hash_code H = hash_combine( // Start with the compiler version (which will be either tag names or @@ -1355,11 +1383,11 @@ InterfaceSubContextDelegateImpl::getCacheHash(StringRef useInterfacePath) { // The SDK path is going to affect how this module is imported, so // include it. - subInvocation.getSDKPath(), + genericSubInvocation.getSDKPath(), // Whether or not we're tracking system dependencies affects the // invalidation behavior of this cache item. - subInvocation.getFrontendOptions().shouldTrackSystemDependencies()); + genericSubInvocation.getFrontendOptions().shouldTrackSystemDependencies()); return llvm::APInt(64, H).toString(36, /*Signed=*/false); } @@ -1368,11 +1396,13 @@ bool InterfaceSubContextDelegateImpl::runInSubContext(StringRef moduleName, StringRef interfacePath, StringRef outputPath, SourceLoc diagLoc, - llvm::function_ref, + llvm::function_ref, ArrayRef, StringRef)> action) { return runInSubCompilerInstance(moduleName, interfacePath, outputPath, diagLoc, [&](SubCompilerInstanceInfo &info){ - return action(info.Instance->getASTContext(), info.BuildArguments, + return action(info.Instance->getASTContext(), + info.Instance->getMainModule(), + info.BuildArguments, info.ExtraPCMArgs, info.Hash); }); @@ -1383,6 +1413,10 @@ bool InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleN StringRef outputPath, SourceLoc diagLoc, llvm::function_ref action) { + // We are about to mess up the compiler invocation by using the compiler + // arguments in the textual interface file. So copy to use a new compiler + // invocation. + CompilerInvocation subInvocation = genericSubInvocation; std::vector BuildArgs(GenericArgs.begin(), GenericArgs.end()); assert(BuildArgs.size() == GenericArgs.size()); // Configure inputs @@ -1417,7 +1451,8 @@ bool InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleN std::string CompilerVersion; // Extract compiler arguments from the interface file and use them to configure // the compiler invocation. - if (extractSwiftInterfaceVersionAndArgs(SubArgs, + if (extractSwiftInterfaceVersionAndArgs(subInvocation, + SubArgs, CompilerVersion, interfacePath, diagLoc)) { @@ -1457,103 +1492,21 @@ bool InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleN } struct ExplicitSwiftModuleLoader::Implementation { - // Information about explicitly specified Swift module files. - struct ExplicitModuleInfo { - // Path of the .swiftmodule file. - StringRef modulePath; - // Path of the .swiftmoduledoc file. - StringRef moduleDocPath; - // Path of the .swiftsourceinfo file. - StringRef moduleSourceInfoPath; - // Opened buffer for the .swiftmodule file. - std::unique_ptr moduleBuffer; - }; ASTContext &Ctx; llvm::BumpPtrAllocator Allocator; - llvm::StringSaver Saver; llvm::StringMap ExplicitModuleMap; - Implementation(ASTContext &Ctx): Ctx(Ctx), Saver(Allocator) {} + Implementation(ASTContext &Ctx) : Ctx(Ctx) {} - StringRef getScalaNodeText(llvm::yaml::Node *N) { - SmallString<32> Buffer; - return Saver.save(cast(N)->getValue(Buffer)); - } - - bool parseSingleModuleEntry(llvm::yaml::Node &node) { - using namespace llvm::yaml; - auto *mapNode = dyn_cast(&node); - if (!mapNode) - return true; - StringRef moduleName; - ExplicitModuleInfo result; - for (auto &entry: *mapNode) { - auto key = getScalaNodeText(entry.getKey()); - auto val = getScalaNodeText(entry.getValue()); - if (key == "moduleName") { - moduleName = val; - } else if (key == "modulePath") { - result.modulePath = val; - } else if (key == "docPath") { - result.moduleDocPath = val; - } else if (key == "sourceInfoPath") { - result.moduleSourceInfoPath = val; - } else { - // Being forgiving for future fields. - continue; - } - } - if (moduleName.empty()) - return true; - ExplicitModuleMap[moduleName] = std::move(result); - return false; - } - // [ - // { - // "moduleName": "A", - // "modulePath": "A.swiftmodule", - // "docPath": "A.swiftdoc", - // "sourceInfoPath": "A.swiftsourceinfo" - // }, - // { - // "moduleName": "B", - // "modulePath": "B.swiftmodule", - // "docPath": "B.swiftdoc", - // "sourceInfoPath": "B.swiftsourceinfo" - // } - // ] void parseSwiftExplicitModuleMap(StringRef fileName) { - using namespace llvm::yaml; - // Load the input file. - llvm::ErrorOr> fileBufOrErr = - llvm::MemoryBuffer::getFile(fileName); - if (!fileBufOrErr) { + ExplicitModuleMapParser parser(Allocator); + auto result = + parser.parseSwiftExplicitModuleMap(fileName, ExplicitModuleMap); + if (result == std::errc::invalid_argument) + Ctx.Diags.diagnose(SourceLoc(), diag::explicit_swift_module_map_corrupted, + fileName); + else if (result == std::errc::no_such_file_or_directory) Ctx.Diags.diagnose(SourceLoc(), diag::explicit_swift_module_map_missing, fileName); - return; - } - StringRef Buffer = fileBufOrErr->get()->getBuffer(); - // Use a new source manager instead of the one from ASTContext because we - // don't want the JSON file to be persistent. - llvm::SourceMgr SM; - Stream Stream(llvm::MemoryBufferRef(Buffer, fileName), SM); - for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { - assert(DI != Stream.end() && "Failed to read a document"); - if (auto *MN = dyn_cast_or_null(DI->getRoot())) { - for (auto &entry: *MN) { - if (parseSingleModuleEntry(entry)) { - Ctx.Diags.diagnose(SourceLoc(), - diag::explicit_swift_module_map_corrupted, - fileName); - return; - } - } - } else { - Ctx.Diags.diagnose(SourceLoc(), - diag::explicit_swift_module_map_corrupted, - fileName); - return; - } - } } }; @@ -1599,6 +1552,31 @@ std::error_code ExplicitSwiftModuleLoader::findModuleFilesInDirectory( moduleInfo.modulePath); return moduleBuf.getError(); } + + assert(moduleBuf); + const bool isForwardingModule = !serialization::isSerializedAST(moduleBuf + .get()->getBuffer()); + // If the module is a forwarding module, read the actual content from the path + // encoded in the forwarding module as the actual module content. + if (isForwardingModule) { + auto forwardingModule = ForwardingModule::load(*moduleBuf.get()); + if (forwardingModule) { + moduleBuf = fs.getBufferForFile(forwardingModule->underlyingModulePath); + if (!moduleBuf) { + // We cannot read the module content, diagnose. + Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_explicit_module_file, + moduleInfo.modulePath); + return moduleBuf.getError(); + } + } else { + // We cannot read the module content, diagnose. + Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_explicit_module_file, + moduleInfo.modulePath); + return forwardingModule.getError(); + } + } + assert(moduleBuf); + // Move the opened module buffer to the caller. *ModuleBuffer = std::move(moduleBuf.get()); // Open .swiftdoc file diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 3b14762c34523..65e094af7ccc8 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -100,11 +100,28 @@ static void printImports(raw_ostream &out, ModuleDecl *M) { // FIXME: This is very similar to what's in Serializer::writeInputBlock, but // it's not obvious what higher-level optimization would be factored out here. + ModuleDecl::ImportFilter allImportFilter = { + ModuleDecl::ImportFilterKind::Public, + ModuleDecl::ImportFilterKind::Private, + ModuleDecl::ImportFilterKind::SPIAccessControl}; + + // With -experimental-spi-imports: + // When printing the private swiftinterface file, print implementation-only + // imports only if they are also SPI. First, list all implementation-only + // imports and filter them later. + llvm::SmallSet ioiImportSet; + if (Opts.PrintSPIs && Opts.ExperimentalSPIImports) { + allImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; + + SmallVector ioiImport; + M->getImportedModules(ioiImport, + ModuleDecl::ImportFilterKind::ImplementationOnly); + ioiImportSet.insert(ioiImport.begin(), ioiImport.end()); + } + SmallVector allImports; - M->getImportedModules(allImports, - {ModuleDecl::ImportFilterKind::Public, - ModuleDecl::ImportFilterKind::Private, - ModuleDecl::ImportFilterKind::SPIAccessControl}); + M->getImportedModules(allImports, allImportFilter); ModuleDecl::removeDuplicateImports(allImports); diagnoseScopedImports(M->getASTContext().Diags, allImports); @@ -124,13 +141,21 @@ static void printImports(raw_ostream &out, continue; } + llvm::SmallSetVector spis; + M->lookupImportedSPIGroups(importedModule, spis); + + // Only print implementation-only imports which have an SPI import. + if (ioiImportSet.count(import)) { + if (spis.empty()) + continue; + out << "@_implementationOnly "; + } + if (publicImportSet.count(import)) out << "@_exported "; // SPI attribute on imports if (Opts.PrintSPIs) { - llvm::SmallSetVector spis; - M->lookupImportedSPIGroups(importedModule, spis); for (auto spiName : spis) out << "@_spi(" << spiName << ") "; } @@ -440,7 +465,7 @@ class InheritedProtocolCollector { printer << " : "; ProtocolDecl *proto = protoAndAvailability.first; - proto->getDeclaredType()->print(printer, printOptions); + proto->getDeclaredInterfaceType()->print(printer, printOptions); printer << " {}\n"; } diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 06347e600b319..37c2f1bf41190 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -45,7 +45,6 @@ #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" -#include "swift/Basic/Timer.h" #include "swift/Basic/UUID.h" #include "swift/Frontend/DiagnosticVerifier.h" #include "swift/Frontend/Frontend.h" @@ -208,6 +207,8 @@ static void emitMakeDependenciesIfNeeded(DiagnosticEngine &diags, }); } +// MARK: - Module Trace + namespace { struct SwiftModuleTraceInfo { Identifier Name; @@ -262,6 +263,318 @@ template <> struct ObjectTraits { } } +static bool isClangOverlayOf(ModuleDecl *potentialOverlay, + ModuleDecl *potentialUnderlying) { + return !potentialOverlay->isNonSwiftModule() + && potentialUnderlying->isNonSwiftModule() + && potentialOverlay->getName() == potentialUnderlying->getName(); +} + +// TODO: Delete this once changes from https://reviews.llvm.org/D83449 land on +// apple/llvm-project's swift/master branch. +template +static bool contains(const SetLike &setLike, Item item) { + return setLike.find(item) != setLike.end(); +} + +/// Get a set of modules imported by \p module. +/// +/// By default, all imports are included. +static void getImmediateImports( + ModuleDecl *module, + SmallPtrSetImpl &imports, + ModuleDecl::ImportFilter importFilter = { + ModuleDecl::ImportFilterKind::Public, + ModuleDecl::ImportFilterKind::Private, + ModuleDecl::ImportFilterKind::ImplementationOnly, + ModuleDecl::ImportFilterKind::SPIAccessControl, + ModuleDecl::ImportFilterKind::ShadowedBySeparateOverlay + }) { + SmallVector importList; + module->getImportedModules(importList, importFilter); + + for (ModuleDecl::ImportedModule &import : importList) + imports.insert(import.importedModule); +} + +namespace { +/// Helper type for computing (approximate) information about ABI-dependencies. +/// +/// This misses out on details such as typealiases and more. +/// See the "isImportedDirectly" field above for more details. +class ABIDependencyEvaluator { + /// Map of ABIs exported by a particular module, excluding itself. + /// + /// For example, consider (primed letters represent Clang modules): + /// \code + /// - A is @_exported-imported by B + /// - B is #imported by C' (via a compiler-generated umbrella header) + /// - C' is @_exported-imported by C (Swift overlay) + /// - D' is #imported by E' + /// - D' is @_exported-imported by D (Swift overlay) + /// - E' is @_exported-imported by E (Swift overlay) + /// \endcode + /// + /// Then the \c abiExportMap will be + /// \code + /// { A: {}, B: {A}, C: {B}, C': {B}, D: {}, D': {}, E: {D}, E': {D'} } + /// \endcode + /// + /// \b WARNING: Use \c reexposeImportedABI instead of inserting directly. + llvm::DenseMap> abiExportMap; + + /// Stack for depth-first traversal. + SmallVector searchStack; + + llvm::DenseSet visited; + + /// Helper function to handle invariant violations as crashes in debug mode. + void crashOnInvariantViolation( + llvm::function_ref f) const; + + /// Computes the ABI exports for \p importedModule and adds them to + /// \p module's ABI exports. + /// + /// If \p includeImportedModule is true, also adds \p importedModule to + /// \p module's ABI exports. + /// + /// Correct way to add entries to \c abiExportMap. + void reexposeImportedABI(ModuleDecl *module, ModuleDecl *importedModule, + bool includeImportedModule = true); + + /// Recursive step in computing ABI dependencies. + /// + /// Use this method instead of using the \c forClangModule/\c forSwiftModule + /// methods. + void computeABIDependenciesForModule(ModuleDecl *module); + void computeABIDependenciesForSwiftModule(ModuleDecl *module); + void computeABIDependenciesForClangModule(ModuleDecl *module); + + static void printModule(const ModuleDecl *module, llvm::raw_ostream &os); + + template + static void printModuleSet(const SetLike &set, llvm::raw_ostream &os); + +public: + ABIDependencyEvaluator() = default; + ABIDependencyEvaluator(const ABIDependencyEvaluator &) = delete; + ABIDependencyEvaluator(ABIDependencyEvaluator &&) = default; + + void getABIDependenciesForSwiftModule( + ModuleDecl *module, SmallPtrSetImpl &abiDependencies); + + void printABIExportMap(llvm::raw_ostream &os) const; +}; +} // end anonymous namespace + +// See [NOTE: Bailing-vs-crashing-in-trace-emission]. +// TODO: Use PrettyStackTrace instead? +void ABIDependencyEvaluator::crashOnInvariantViolation( + llvm::function_ref f) const { +#ifndef NDEBUG + std::string msg; + llvm::raw_string_ostream os(msg); + os << "error: invariant violation: "; + f(os); + llvm::report_fatal_error(os.str()); +#endif +} + +// [NOTE: Trace-Clang-submodule-complexity] +// +// A Clang module may have zero or more submodules. In practice, when traversing +// the imports of a module, we observe that different submodules of the same +// top-level module (almost) freely import each other. Despite this, we still +// need to conceptually traverse the tree formed by the submodule relationship +// (with the top-level module being the root). +// +// This needs to be taken care of in two ways: +// 1. We need to make sure we only go towards the leaves. It's okay if we "jump" +// branches, so long as we don't try to visit an ancestor when one of its +// descendants is still on the traversal stack, so that we don't end up with +// arbitrarily complex intra-module cycles. +// See also: [NOTE: Intra-module-leafwards-traversal]. +// 2. When adding entries to the ABI export map, we need to avoid marking +// dependencies within the same top-level module. This step is needed in +// addition to step 1 to avoid creating cycles like +// Overlay -> Underlying -> Submodule -> Overlay. + +void ABIDependencyEvaluator::reexposeImportedABI( + ModuleDecl *module, ModuleDecl *importedModule, + bool includeImportedModule) { + if (module == importedModule) { + crashOnInvariantViolation([&](llvm::raw_string_ostream &os) { + os << "module "; printModule(module, os); os << " imports itself!\n"; + }); + return; + } + + auto addToABIExportMap = [this](ModuleDecl *module, ModuleDecl *reexport) { + if (module == reexport) { + crashOnInvariantViolation([&](llvm::raw_string_ostream &os){ + os << "expected module "; printModule(reexport, os); + os << " to not re-export itself\n"; + }); + return; + } + if (reexport->isNonSwiftModule() + && module->isNonSwiftModule() + && module->getTopLevelModule() == reexport->getTopLevelModule()) { + // Dependencies within the same top-level Clang module are not useful. + // See also: [NOTE: Trace-Clang-submodule-complexity]. + return; + } + + // We only care about dependencies across top-level modules and we want to + // avoid exploding abiExportMap with submodules. So we only insert entries + // after calling getTopLevelModule(). + + if (::isClangOverlayOf(module, reexport)) { + // For overlays, we need to have a dependency on the underlying module. + // Otherwise, we might accidentally create a Swift -> Swift cycle. + abiExportMap[module].insert( + reexport->getTopLevelModule(/*preferOverlay*/false)); + return; + } + abiExportMap[module].insert( + reexport->getTopLevelModule(/*preferOverlay*/true)); + }; + + computeABIDependenciesForModule(importedModule); + if (includeImportedModule) { + addToABIExportMap(module, importedModule); + } + // Force creation of default value if missing. This prevents abiExportMap from + // growing (and moving) when calling addToABIExportMap. If abiExportMap gets + // moved, then abiExportMap[importedModule] will be moved, forcing us to + // create a defensive copy to avoid iterator invalidation on move. + (void)abiExportMap[module]; + for (auto reexportedModule: abiExportMap[importedModule]) + addToABIExportMap(module, reexportedModule); +} + +void ABIDependencyEvaluator::computeABIDependenciesForModule( + ModuleDecl *module) { + if (llvm::find(searchStack, module) != searchStack.end()) { + crashOnInvariantViolation([&](llvm::raw_string_ostream &os) { + os << "unexpected cycle in import graph!\n"; + for (auto m: searchStack) { + printModule(m, os); os << "\ndepends on "; + } + printModule(module, os); os << '\n'; + }); + return; + } + if (::contains(visited, module)) + return; + searchStack.push_back(module); + if (module->isNonSwiftModule()) + computeABIDependenciesForClangModule(module); + else + computeABIDependenciesForSwiftModule(module); + searchStack.pop_back(); + visited.insert(module); +} + +void ABIDependencyEvaluator::computeABIDependenciesForSwiftModule( + ModuleDecl *module) { + SmallPtrSet allImports; + ::getImmediateImports(module, allImports); + for (auto import: allImports) { + computeABIDependenciesForModule(import); + if (::isClangOverlayOf(module, import)) { + reexposeImportedABI(module, import, + /*includeImportedModule=*/false); + } + } + + SmallPtrSet reexportedImports; + ::getImmediateImports(module, reexportedImports, + {ModuleDecl::ImportFilterKind::Public}); + for (auto reexportedImport: reexportedImports) { + reexposeImportedABI(module, reexportedImport); + } +} + +void ABIDependencyEvaluator::computeABIDependenciesForClangModule( + ModuleDecl *module) { + SmallPtrSet imports; + ::getImmediateImports(module, imports); + for (auto import: imports) { + // There are three cases here which can potentially create cycles: + // + // 1. Clang modules importing the stdlib. + // See [NOTE: Pure-Clang-modules-privately-import-stdlib]. + // 2. Overlay S @_exported-imports underlying module S' and another Clang + // module C'. C' (transitively) #imports S' but it gets treated as if + // C' imports S. This creates a cycle: S -> C' -> ... -> S. + // In practice, this case is hit for + // Darwin (Swift) -> SwiftOverlayShims (Clang) -> Darwin (Swift). + // 3. [NOTE: Intra-module-leafwards-traversal] + // Cycles within the same top-level module. + // These don't matter for us, since we only care about the dependency + // graph at the granularity of top-level modules. So we ignore these + // by only considering parent -> submodule dependencies. + // See also [NOTE: Trace-Clang-submodule-complexity]. + if (import->isStdlibModule()) { + continue; + } + if (!import->isNonSwiftModule() + && import->findUnderlyingClangModule() != nullptr + && llvm::find(searchStack, import) != searchStack.end()) { + continue; + } + if (import->isNonSwiftModule() + && module->getTopLevelModule() == import->getTopLevelModule() + && !import->findUnderlyingClangModule() + ->isSubModuleOf(module->findUnderlyingClangModule())) { + continue; + } + computeABIDependenciesForModule(import); + reexposeImportedABI(module, import); + } +} + +void ABIDependencyEvaluator::getABIDependenciesForSwiftModule( + ModuleDecl *module, SmallPtrSetImpl &abiDependencies) { + computeABIDependenciesForModule(module); + SmallPtrSet allImports; + ::getImmediateImports(module, allImports); + for (auto directDependency: allImports) { + abiDependencies.insert(directDependency); + for (auto exposedDependency: abiExportMap[directDependency]) { + abiDependencies.insert(exposedDependency); + } + } +} + +void ABIDependencyEvaluator::printModule( + const ModuleDecl *module, llvm::raw_ostream &os) { + module->getReverseFullModuleName().printForward(os); + os << (module->isNonSwiftModule() ? " (Clang)" : " (Swift)"); + os << " @ " << llvm::format("0x%llx", reinterpret_cast(module)); +} + +template +void ABIDependencyEvaluator::printModuleSet( + const SetLike &set, llvm::raw_ostream &os) { + os << "{ "; + for (auto module: set) { + printModule(module, os); os << ", "; + } + os << "}"; +} + +void ABIDependencyEvaluator::printABIExportMap(llvm::raw_ostream &os) const { + os << "ABI Export Map {{\n"; + for (auto &entry: abiExportMap) { + printModule(entry.first, os); os << " : "; + printModuleSet(entry.second, os); + os << "\n"; + } + os << "}}\n"; +} + /// Compute the per-module information to be recorded in the trace file. // // The most interesting/tricky thing here is _which_ paths get recorded in @@ -276,7 +589,7 @@ template <> struct ObjectTraits { // FIXME: Use the VFS instead of handling paths directly. We are particularly // sloppy about handling relative paths in the dependency tracker. static void computeSwiftModuleTraceInfo( - const SmallPtrSetImpl &importedModules, + const SmallPtrSetImpl &abiDependencies, const llvm::DenseMap &pathToModuleDecl, const DependencyTracker &depTracker, StringRef prebuiltCachePath, @@ -337,6 +650,8 @@ static void computeSwiftModuleTraceInfo( // this is good enough. : buffer.str(); + bool isImportedDirectly = ::contains(abiDependencies, depMod); + traceInfo.push_back( {/*Name=*/ depMod->getName(), @@ -347,8 +662,10 @@ static void computeSwiftModuleTraceInfo( // app/test using -import-objc-header, we should look at the direct // imports of the bridging modules, and mark those as our direct // imports. + // TODO: Add negative test cases for the comment above. + // TODO: Describe precise semantics of "isImportedDirectly". /*IsImportedDirectly=*/ - importedModules.find(depMod) != importedModules.end(), + isImportedDirectly, /*SupportsLibraryEvolution=*/ depMod->isResilient()}); buffer.clear(); @@ -376,8 +693,7 @@ static void computeSwiftModuleTraceInfo( // be saved (not checked), so don't save the path to this swiftmodule. SmallString<256> moduleAdjacentInterfacePath(depPath); computeAdjacentInterfacePath(moduleAdjacentInterfacePath); - if (pathToModuleDecl.find(moduleAdjacentInterfacePath) - != pathToModuleDecl.end()) + if (::contains(pathToModuleDecl, moduleAdjacentInterfacePath)) continue; // FIXME: The behavior of fs::exists for relative paths is undocumented. @@ -404,6 +720,18 @@ static void computeSwiftModuleTraceInfo( }); } +// [NOTE: Bailing-vs-crashing-in-trace-emission] There are certain edge cases +// in trace emission where an invariant that you think should hold does not hold +// in practice. For example, sometimes we have seen modules without any +// corresponding filename. +// +// Since the trace is a supplementary output for build system consumption, it +// it better to emit it on a best-effort basis instead of crashing and failing +// the build. +// +// Moreover, going forward, it would be nice if trace emission were more robust +// so we could emit the trace on a best-effort basis even if the dependency +// graph is ill-formed, so that the trace can be used as a debugging aid. static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, DependencyTracker *depTracker, StringRef prebuiltCachePath, @@ -424,17 +752,12 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, return true; } - ModuleDecl::ImportFilter filter = ModuleDecl::ImportFilterKind::Public; - filter |= ModuleDecl::ImportFilterKind::Private; - filter |= ModuleDecl::ImportFilterKind::ImplementationOnly; - filter |= ModuleDecl::ImportFilterKind::SPIAccessControl; - filter |= ModuleDecl::ImportFilterKind::ShadowedBySeparateOverlay; - SmallVector imports; - mainModule->getImportedModules(imports, filter); - - SmallPtrSet importedModules; - for (ModuleDecl::ImportedModule &import : imports) - importedModules.insert(import.importedModule); + SmallPtrSet abiDependencies; + { + ABIDependencyEvaluator evaluator{}; + evaluator.getABIDependenciesForSwiftModule(mainModule, + abiDependencies); + } llvm::DenseMap pathToModuleDecl; for (const auto &module : ctxt.getLoadedModules()) { @@ -458,7 +781,8 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, } std::vector swiftModules; - computeSwiftModuleTraceInfo(importedModules, pathToModuleDecl, *depTracker, + computeSwiftModuleTraceInfo(abiDependencies, + pathToModuleDecl, *depTracker, prebuiltCachePath, swiftModules); LoadedModuleTraceFormat trace = { @@ -1054,9 +1378,9 @@ static bool writeLdAddCFileIfNeeded(CompilerInstance &Instance) { } auto tbdOpts = Invocation.getTBDGenOptions(); tbdOpts.LinkerDirectivesOnly = true; - llvm::StringSet<> ldSymbols; auto *module = Instance.getMainModule(); - enumeratePublicSymbols(module, ldSymbols, tbdOpts); + auto ldSymbols = + getPublicSymbols(TBDGenDescriptor::forModule(module, tbdOpts)); std::error_code EC; llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); if (EC) { @@ -1074,7 +1398,7 @@ static bool writeLdAddCFileIfNeeded(CompilerInstance &Instance) { llvm::raw_svector_ostream NameOS(NameBuffer); NameOS << "ldAdd_" << Idx; OS << "extern const char " << NameOS.str() << " __asm(\"" << - changeToLdAdd(S.getKey()) << "\");\n"; + changeToLdAdd(S) << "\");\n"; OS << "const char " << NameOS.str() << " = 0;\n"; ++ Idx; } @@ -1501,24 +1825,21 @@ static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, } static GeneratedModule -generateIR(const IRGenOptions &IRGenOpts, +generateIR(const IRGenOptions &IRGenOpts, const TBDGenOptions &TBDOpts, std::unique_ptr SM, const PrimarySpecificPaths &PSPs, StringRef OutputFilename, ModuleOrSourceFile MSF, llvm::GlobalVariable *&HashGlobal, - ArrayRef parallelOutputFilenames, - llvm::StringSet<> &LinkerDirectives) { + ArrayRef parallelOutputFilenames) { if (auto *SF = MSF.dyn_cast()) { - return performIRGeneration(IRGenOpts, *SF, + return performIRGeneration(SF, IRGenOpts, TBDOpts, std::move(SM), OutputFilename, PSPs, SF->getPrivateDiscriminator().str(), - &HashGlobal, - &LinkerDirectives); + &HashGlobal); } else { - return performIRGeneration(IRGenOpts, MSF.get(), + return performIRGeneration(MSF.get(), IRGenOpts, TBDOpts, std::move(SM), OutputFilename, PSPs, - parallelOutputFilenames, - &HashGlobal, &LinkerDirectives); + parallelOutputFilenames, &HashGlobal); } } @@ -1651,27 +1972,18 @@ static bool generateCode(CompilerInstance &Instance, StringRef OutputFilename, const auto &opts = Instance.getInvocation().getIRGenOptions(); std::unique_ptr TargetMachine = createTargetMachine(opts, Instance.getASTContext()); - version::Version EffectiveLanguageVersion = - Instance.getASTContext().LangOpts.EffectiveLanguageVersion; // Free up some compiler resources now that we have an IRModule. freeASTContextIfPossible(Instance); + // If we emitted any errors while perfoming the end-of-pipeline actions, bail. + if (Instance.getDiags().hadAnyError()) + return true; + // Now that we have a single IR Module, hand it over to performLLVM. return performLLVM(opts, Instance.getDiags(), nullptr, HashGlobal, IRModule, - TargetMachine.get(), EffectiveLanguageVersion, - OutputFilename, Instance.getStatsReporter()); -} - -static void collectLinkerDirectives(const 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); + TargetMachine.get(), OutputFilename, + Instance.getStatsReporter()); } static bool performCompileStepsPostSILGen(CompilerInstance &Instance, @@ -1781,34 +2093,25 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance, return processCommandLineAndRunImmediately( Instance, std::move(SM), MSF, observer, ReturnValue); - llvm::StringSet<> LinkerDirectives; - collectLinkerDirectives(Invocation, MSF, LinkerDirectives); - // Don't proceed to IRGen if collecting linker directives failed. - if (Context.hadError()) - return true; StringRef OutputFilename = PSPs.OutputFilename; std::vector ParallelOutputFilenames = opts.InputsAndOutputs.copyOutputFilenames(); llvm::GlobalVariable *HashGlobal; auto IRModule = generateIR( - IRGenOpts, std::move(SM), PSPs, OutputFilename, MSF, HashGlobal, - ParallelOutputFilenames, LinkerDirectives); - - // Just because we had an AST error it doesn't mean we can't performLLVM. - bool HadError = Instance.getASTContext().hadError(); + IRGenOpts, Invocation.getTBDGenOptions(), std::move(SM), PSPs, + OutputFilename, MSF, HashGlobal, ParallelOutputFilenames); - // If the AST Context has no errors but no IRModule is available, - // parallelIRGen happened correctly, since parallel IRGen produces multiple - // modules. + // If no IRModule is available, bail. This can either happen if IR generation + // fails, or if parallelIRGen happened correctly (in which case it would have + // already performed LLVM). if (!IRModule) - return HadError; + return Instance.getDiags().hadAnyError(); if (validateTBDIfNeeded(Invocation, MSF, *IRModule.getModule())) return true; return generateCode(Instance, OutputFilename, IRModule.getModule(), - HashGlobal) || - HadError; + HashGlobal); } static void emitIndexDataForSourceFile(SourceFile *PrimarySourceFile, @@ -2159,17 +2462,18 @@ int swift::performFrontend(ArrayRef Args, } CompilerInvocation Invocation; - std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(Argv0, - MainAddr); - Invocation.setMainExecutablePath(MainExecutablePath); SmallString<128> workingDirectory; llvm::sys::fs::current_path(workingDirectory); + std::string MainExecutablePath = + llvm::sys::fs::getMainExecutable(Argv0, MainAddr); + // Parse arguments. SmallVector, 4> configurationFileBuffers; if (Invocation.parseArgs(Args, Instance->getDiags(), - &configurationFileBuffers, workingDirectory)) { + &configurationFileBuffers, workingDirectory, + MainExecutablePath)) { return finishDiagProcessing(1, /*verifierEnabled*/ false); } @@ -2253,9 +2557,6 @@ int swift::performFrontend(ArrayRef Args, PDC.setFormattingStyle( Invocation.getDiagnosticOptions().PrintedFormattingStyle); - if (Invocation.getFrontendOptions().DebugTimeCompilation) - SharedTimer::enableCompilationTimers(); - if (Invocation.getFrontendOptions().PrintStats) { llvm::EnableStatistics(); } diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index b8f34d9defabe..6a90ae8231c86 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -62,23 +62,12 @@ static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName, /// Resolve the direct dependencies of the given module. static std::vector resolveDirectDependencies( CompilerInstance &instance, ModuleDependencyID module, - ModuleDependenciesCache &cache) { + ModuleDependenciesCache &cache, + InterfaceSubContextDelegate &ASTDelegate) { auto &ctx = instance.getASTContext(); auto knownDependencies = *cache.findDependencies(module.first, module.second); auto isSwift = knownDependencies.isSwiftModule(); - auto ModuleCachePath = getModuleCachePathFromClang(ctx - .getClangModuleLoader()->getClangInstance()); - auto &FEOpts = instance.getInvocation().getFrontendOptions(); - ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); - InterfaceSubContextDelegateImpl ASTDelegate(ctx.SourceMgr, ctx.Diags, - ctx.SearchPathOpts, ctx.LangOpts, - LoaderOpts, - ctx.getClangModuleLoader(), - /*buildModuleCacheDirIfAbsent*/false, - ModuleCachePath, - FEOpts.PrebuiltModuleCachePath, - FEOpts.SerializeModuleInterfaceDependencyHashes, - FEOpts.shouldTrackSystemDependencies()); + // Find the dependencies of every module this module directly depends on. std::vector result; for (auto dependsOn : knownDependencies.getModuleDependencies()) { @@ -138,39 +127,75 @@ static std::vector resolveDirectDependencies( } } } - // Only resolve cross-import overlays when this is the main module. - // For other modules, these overlays are explicitly written. - bool isMainModule = - instance.getMainModule()->getName().str() == module.first && - module.second == ModuleDependenciesKind::Swift; - if (isMainModule) { - // Modules explicitly imported. Only these can be secondary module. - std::vector explicitImports = result; - for (unsigned I = 0; I != result.size(); ++I) { - auto dep = result[I]; - auto moduleName = dep.first; - auto dependencies = *cache.findDependencies(moduleName, dep.second); - // Collect a map from secondary module name to cross-import overlay names. - auto overlayMap = dependencies.collectCrossImportOverlayNames( - instance.getASTContext(), moduleName); - if (overlayMap.empty()) - continue; - std::for_each(explicitImports.begin(), explicitImports.end(), - [&](ModuleDependencyID Id) { - // check if any explicitly imported modules can serve as a secondary - // module, and add the overlay names to the dependencies list. - for (auto overlayName: overlayMap[Id.first]) { - if (auto found = ctx.getModuleDependencies(overlayName.str(), - /*onlyClangModule=*/false, - cache, - ASTDelegate)) { - result.emplace_back(overlayName.str(), found->getKind()); - } + return result; +} + +static void discoverCrosssImportOverlayDependencies( + CompilerInstance &instance, StringRef mainModuleName, + ArrayRef allDependencies, + ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate, + llvm::function_ref action) { + // Modules explicitly imported. Only these can be secondary module. + llvm::SetVector newOverlays; + for (auto dep: allDependencies) { + auto moduleName = dep.first; + auto dependencies = *cache.findDependencies(moduleName, dep.second); + // Collect a map from secondary module name to cross-import overlay names. + auto overlayMap = dependencies.collectCrossImportOverlayNames( + instance.getASTContext(), moduleName); + if (overlayMap.empty()) + continue; + std::for_each(allDependencies.begin(), allDependencies.end(), + [&](ModuleDependencyID Id) { + // check if any explicitly imported modules can serve as a secondary + // module, and add the overlay names to the dependencies list. + for (auto overlayName: overlayMap[Id.first]) { + if (std::find_if(allDependencies.begin(), allDependencies.end(), + [&](ModuleDependencyID Id) { return Id.first == overlayName.str(); }) + == allDependencies.end()) { + newOverlays.insert(overlayName); } - }); - } + } + }); } - return result; + // No new cross-import overlays are found, return. + if (newOverlays.empty()) + return; + // Construct a dummy main to resolve the newly discovered cross import overlays. + StringRef dummyMainName = "DummyMainModuleForResolvingCrossImportOverlays"; + auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); + + // Update main module's dependencies to include these new overlays. + auto mainDep = *cache.findDependencies(mainModuleName, ModuleDependenciesKind::Swift); + std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { + dummyMainDependencies.addModuleDependency(modName.str()); + mainDep.addModuleDependency(modName.str()); + }); + cache.updateDependencies({mainModuleName, ModuleDependenciesKind::Swift}, mainDep); + + // Record the dummy main module's direct dependencies. The dummy main module + // only directly depend on these newly discovered overlay modules. + cache.recordDependencies(dummyMainName, dummyMainDependencies, + ModuleDependenciesKind::Swift); + llvm::SetVector, + std::set> allModules; + + // Seed the all module list from the dummpy main module. + allModules.insert({dummyMainName.str(), dummyMainDependencies.getKind()}); + + // Explore the dependencies of every module. + for (unsigned currentModuleIdx = 0; + currentModuleIdx < allModules.size(); + ++currentModuleIdx) { + auto module = allModules[currentModuleIdx]; + auto discoveredModules = resolveDirectDependencies(instance, module, + cache, ASTDelegate); + allModules.insert(discoveredModules.begin(), discoveredModules.end()); + } + // Report any discovered modules to the clients, which include all overlays + // and their dependencies. + std::for_each(/* +1 to exclude dummy main*/allModules.begin() + 1, + allModules.end(), action); } /// Write a single JSON field. @@ -196,10 +221,17 @@ namespace { const ModuleDependencyID &module, unsigned indentLevel) { out << "{\n"; + std::string moduleKind; + if (module.second == ModuleDependenciesKind::Swift) + moduleKind = "swift"; + else if (module.second == ModuleDependenciesKind::SwiftPlaceholder) + moduleKind = "swiftPlaceholder"; + else + moduleKind = "clang"; writeJSONSingleField( out, - module.second == ModuleDependenciesKind::Swift ? "swift" : "clang", + moduleKind, module.first, indentLevel + 1, /*trailingComma=*/false); @@ -259,6 +291,7 @@ namespace { static void writeJSON(llvm::raw_ostream &out, CompilerInstance &instance, ModuleDependenciesCache &cache, + InterfaceSubContextDelegate &ASTDelegate, ArrayRef allModules) { // Write out a JSON description of all of the dependencies. out << "{\n"; @@ -277,7 +310,8 @@ static void writeJSON(llvm::raw_ostream &out, }; for (const auto &module : allModules) { auto directDependencies = resolveDirectDependencies( - instance, ModuleDependencyID(module.first, module.second), cache); + instance, ModuleDependencyID(module.first, module.second), cache, + ASTDelegate); // Grab the completed module dependencies. auto moduleDeps = *cache.findDependencies(module.first, module.second); @@ -290,27 +324,33 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(2 * 2); out << "{\n"; + auto externalSwiftDep = moduleDeps.getAsPlaceholderDependencyModule(); + auto swiftDeps = moduleDeps.getAsSwiftModule(); + auto clangDeps = moduleDeps.getAsClangModule(); + // Module path. const char *modulePathSuffix = moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm"; - std::string modulePath = module.first + modulePathSuffix; + + std::string modulePath = externalSwiftDep + ? externalSwiftDep->compiledModulePath + : module.first + modulePathSuffix; writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, /*trailingComma=*/true); // Source files. - auto swiftDeps = moduleDeps.getAsSwiftModule(); - auto clangDeps = moduleDeps.getAsClangModule(); if (swiftDeps) { writeJSONSingleField(out, "sourceFiles", swiftDeps->sourceFiles, 3, - /*trailingComma=*/true); - } else { + /*trailingComma=*/true); + } else if (clangDeps) { writeJSONSingleField(out, "sourceFiles", clangDeps->fileDependencies, 3, /*trailingComma=*/true); } // Direct dependencies. - writeJSONSingleField(out, "directDependencies", directDependencies, - 3, /*trailingComma=*/true); + if (swiftDeps || clangDeps) + writeJSONSingleField(out, "directDependencies", directDependencies, 3, + /*trailingComma=*/true); // Swift and Clang-specific details. out.indent(3 * 2); @@ -331,6 +371,7 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(5 * 2); out << "\"commandLine\": [\n"; for (auto &arg :swiftDeps->buildCommandLine) { + out.indent(6 * 2); out << "\"" << arg << "\""; if (&arg != &swiftDeps->buildCommandLine.back()) @@ -339,6 +380,17 @@ static void writeJSON(llvm::raw_ostream &out, } out.indent(5 * 2); out << "],\n"; + out.indent(5 * 2); + out << "\"compiledModuleCandidates\": [\n"; + for (auto &candidate: swiftDeps->compiledModuleCandidates) { + out.indent(6 * 2); + out << "\"" << candidate << "\""; + if (&candidate != &swiftDeps->compiledModuleCandidates.back()) + out << ","; + out << "\n"; + } + out.indent(5 * 2); + out << "],\n"; } else if (!swiftDeps->compiledModulePath.empty()) { writeJSONSingleField( out, "compiledModulePath", @@ -348,7 +400,7 @@ static void writeJSON(llvm::raw_ostream &out, if (!swiftDeps->extraPCMArgs.empty()) { out.indent(5 * 2); out << "\"extraPcmArgs\": [\n"; - for (auto &arg :swiftDeps->extraPCMArgs) { + for (auto &arg : swiftDeps->extraPCMArgs) { out.indent(6 * 2); out << "\"" << arg << "\""; if (&arg != &swiftDeps->extraPCMArgs.back()) @@ -362,11 +414,10 @@ static void writeJSON(llvm::raw_ostream &out, if (swiftDeps->bridgingHeaderFile) { out.indent(5 * 2); out << "\"bridgingHeader\": {\n"; - writeJSONSingleField(out, "path", - *swiftDeps->bridgingHeaderFile, 6, + writeJSONSingleField(out, "path", *swiftDeps->bridgingHeaderFile, 6, /*trailingComma=*/true); - writeJSONSingleField(out, "sourceFiles", - swiftDeps->bridgingSourceFiles, 6, + writeJSONSingleField(out, "sourceFiles", swiftDeps->bridgingSourceFiles, + 6, /*trailingComma=*/true); writeJSONSingleField(out, "moduleDependencies", swiftDeps->bridgingModuleDependencies, 6, @@ -374,22 +425,35 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(5 * 2); out << "}\n"; } + } else if (externalSwiftDep) { + out << "\"swiftPlaceholder\": {\n"; + + // Module doc file + if (externalSwiftDep->moduleDocPath != "") + writeJSONSingleField(out, "moduleDocPath", + externalSwiftDep->moduleDocPath, + /*indentLevel=*/5, + /*trailingComma=*/true); + + // Module Source Info file + if (externalSwiftDep->moduleDocPath != "") + writeJSONSingleField(out, "moduleSourceInfoPath", + externalSwiftDep->sourceInfoPath, + /*indentLevel=*/5, + /*trailingComma=*/true); } else { out << "\"clang\": {\n"; // Module map file. - writeJSONSingleField(out, "moduleMapPath", - clangDeps->moduleMapFile, 5, + writeJSONSingleField(out, "moduleMapPath", clangDeps->moduleMapFile, 5, /*trailingComma=*/true); // Context hash. - writeJSONSingleField(out, "contextHash", - clangDeps->contextHash, 5, + writeJSONSingleField(out, "contextHash", clangDeps->contextHash, 5, /*trailingComma=*/true); // Command line. - writeJSONSingleField(out, "commandLine", - clangDeps->nonPathCommandLine, 5, + writeJSONSingleField(out, "commandLine", clangDeps->nonPathCommandLine, 5, /*trailingComma=*/false); } @@ -397,7 +461,6 @@ static void writeJSON(llvm::raw_ostream &out, out << "}\n"; out.indent(3 * 2); out << "}\n"; - out.indent(2 * 2); out << "}"; @@ -407,6 +470,57 @@ static void writeJSON(llvm::raw_ostream &out, } } +static bool diagnoseCycle(CompilerInstance &instance, + ModuleDependenciesCache &cache, + ModuleDependencyID mainId, + InterfaceSubContextDelegate &astDelegate) { + llvm::SetVector, + std::set> openSet; + llvm::SetVector, + std::set> closeSet; + // Start from the main module. + openSet.insert(mainId); + while(!openSet.empty()) { + auto &lastOpen = openSet.back(); + auto beforeSize = openSet.size(); + for (auto dep: resolveDirectDependencies(instance, lastOpen, cache, + astDelegate)) { + if (closeSet.count(dep)) + continue; + if (openSet.insert(dep)) { + break; + } else { + // Find a cycle, diagnose. + auto startIt = std::find(openSet.begin(), openSet.end(), dep); + assert(startIt != openSet.end()); + llvm::SmallString<64> buffer; + for (auto it = startIt; it != openSet.end(); ++ it) { + buffer.append(it->first); + buffer.append(it->second == ModuleDependenciesKind::Swift? + ".swiftmodule": ".pcm"); + buffer.append(" -> "); + } + buffer.append(startIt->first); + buffer.append(startIt->second == ModuleDependenciesKind::Swift? + ".swiftmodule": ".pcm"); + instance.getASTContext().Diags.diagnose(SourceLoc(), + diag::scanner_find_cycle, + buffer.str()); + return true; + } + } + // No new node added. We can close this node + if (openSet.size() == beforeSize) { + closeSet.insert(openSet.back()); + openSet.pop_back(); + } else { + assert(openSet.size() == beforeSize + 1); + } + } + assert(openSet.empty()); + return false; +} + bool swift::scanDependencies(CompilerInstance &instance) { ASTContext &Context = instance.getASTContext(); ModuleDecl *mainModule = instance.getMainModule(); @@ -458,18 +572,18 @@ bool swift::scanDependencies(CompilerInstance &instance) { break; case ImplicitStdlibKind::Stdlib: - mainDependencies.addModuleDependency("Swift", alreadyAddedModules); + mainDependencies.addModuleDependency("Swift", &alreadyAddedModules); break; } // Add any implicit module names. for (const auto &moduleName : importInfo.ModuleNames) { - mainDependencies.addModuleDependency(moduleName.str(), alreadyAddedModules); + mainDependencies.addModuleDependency(moduleName.str(), &alreadyAddedModules); } // Already-loaded, implicitly imported module names. for (const auto &module : importInfo.AdditionalModules) { - mainDependencies.addModuleDependency(module.first->getNameStr(), alreadyAddedModules); + mainDependencies.addModuleDependency(module.first->getNameStr(), &alreadyAddedModules); } // Add the bridging header. @@ -481,7 +595,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { // add a dependency with the same name to trigger the search. if (importInfo.ShouldImportUnderlyingModule) { mainDependencies.addModuleDependency(mainModule->getName().str(), - alreadyAddedModules); + &alreadyAddedModules); } } @@ -497,18 +611,44 @@ bool swift::scanDependencies(CompilerInstance &instance) { cache.recordDependencies(mainModuleName, std::move(mainDependencies), ModuleDependenciesKind::Swift); + auto &ctx = instance.getASTContext(); + auto ModuleCachePath = getModuleCachePathFromClang(ctx + .getClangModuleLoader()->getClangInstance()); + auto &FEOpts = instance.getInvocation().getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + InterfaceSubContextDelegateImpl ASTDelegate(ctx.SourceMgr, ctx.Diags, + ctx.SearchPathOpts, ctx.LangOpts, + LoaderOpts, + ctx.getClangModuleLoader(), + /*buildModuleCacheDirIfAbsent*/false, + ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, + FEOpts.SerializeModuleInterfaceDependencyHashes, + FEOpts.shouldTrackSystemDependencies()); + // Explore the dependencies of every module. for (unsigned currentModuleIdx = 0; currentModuleIdx < allModules.size(); ++currentModuleIdx) { auto module = allModules[currentModuleIdx]; auto discoveredModules = - resolveDirectDependencies(instance, module, cache); + resolveDirectDependencies(instance, module, cache, ASTDelegate); allModules.insert(discoveredModules.begin(), discoveredModules.end()); } + // We have all explicit imports now, resolve cross import overlays. + discoverCrosssImportOverlayDependencies(instance, mainModuleName, + /*All transitive dependencies*/allModules.getArrayRef().slice(1), cache, + ASTDelegate, [&](ModuleDependencyID id) { + allModules.insert(id); + }); + + // Dignose cycle in dependency graph. + if (diagnoseCycle(instance, cache, /*MainModule*/allModules.front(), ASTDelegate)) + return true; + // Write out the JSON description. - writeJSON(out, instance, cache, allModules.getArrayRef()); + writeJSON(out, instance, cache, ASTDelegate, allModules.getArrayRef()); // Update the dependency tracker. if (auto depTracker = instance.getDependencyTracker()) { @@ -524,8 +664,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { depTracker->addDependency(sourceFile, /*IsSystem=*/false); for (const auto &bridgingSourceFile : swiftDeps->bridgingSourceFiles) depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false); - } else { - auto clangDeps = deps->getAsClangModule(); + } else if (auto clangDeps = deps->getAsClangModule()) { if (!clangDeps->moduleMapFile.empty()) depTracker->addDependency(clangDeps->moduleMapFile, /*IsSystem=*/false); for (const auto &sourceFile : clangDeps->fileDependencies) diff --git a/lib/FrontendTool/TBD.cpp b/lib/FrontendTool/TBD.cpp index f466c8e5b94d5..77a27a2152e87 100644 --- a/lib/FrontendTool/TBD.cpp +++ b/lib/FrontendTool/TBD.cpp @@ -73,10 +73,13 @@ bool swift::inputFileKindCanHaveTBDValidated(InputFileKind kind) { llvm_unreachable("unhandled kind"); } -static bool validateSymbolSet(DiagnosticEngine &diags, - llvm::StringSet<> symbols, - const llvm::Module &IRModule, - bool diagnoseExtraSymbolsInTBD) { +static bool validateSymbols(DiagnosticEngine &diags, + const std::vector &symbols, + const llvm::Module &IRModule, + bool diagnoseExtraSymbolsInTBD) { + llvm::StringSet<> symbolSet; + symbolSet.insert(symbols.begin(), symbols.end()); + auto error = false; // Diff the two sets of symbols, flagging anything outside their intersection. @@ -101,13 +104,13 @@ static bool validateSymbolSet(DiagnosticEngine &diags, GV->hasExternalLinkage() && !GV->hasHiddenVisibility(); if (!GV->isDeclaration() && externallyVisible) { // Is it listed? - if (!symbols.erase(name)) + if (!symbolSet.erase(name)) // Note: Add the unmangled name to the irNotTBD list, which is owned // by the IRModule, instead of the mangled name. irNotTBD.push_back(unmangledName); } } else { - assert(symbols.find(name) == symbols.end() && + assert(symbolSet.find(name) == symbolSet.end() && "non-global value in value symbol table"); } } @@ -121,7 +124,7 @@ static bool validateSymbolSet(DiagnosticEngine &diags, if (diagnoseExtraSymbolsInTBD) { // Look for any extra symbols. - for (auto &name : sortSymbols(symbols)) { + for (auto &name : sortSymbols(symbolSet)) { diags.diagnose(SourceLoc(), diag::symbol_in_tbd_not_in_ir, name, Demangle::demangleSymbolAsString(name)); error = true; @@ -139,21 +142,16 @@ bool swift::validateTBD(ModuleDecl *M, const llvm::Module &IRModule, const TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD) { - llvm::StringSet<> symbols; - enumeratePublicSymbols(M, symbols, opts); - - return validateSymbolSet(M->getASTContext().Diags, symbols, IRModule, - diagnoseExtraSymbolsInTBD); + auto symbols = getPublicSymbols(TBDGenDescriptor::forModule(M, opts)); + return validateSymbols(M->getASTContext().Diags, symbols, IRModule, + diagnoseExtraSymbolsInTBD); } bool swift::validateTBD(FileUnit *file, const llvm::Module &IRModule, const TBDGenOptions &opts, bool diagnoseExtraSymbolsInTBD) { - llvm::StringSet<> symbols; - enumeratePublicSymbols(file, symbols, opts); - - return validateSymbolSet(file->getParentModule()->getASTContext().Diags, - symbols, IRModule, - diagnoseExtraSymbolsInTBD); + auto symbols = getPublicSymbols(TBDGenDescriptor::forFile(file, opts)); + return validateSymbols(file->getParentModule()->getASTContext().Diags, + symbols, IRModule, diagnoseExtraSymbolsInTBD); } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 049f2a311680e..d3eb0b21c6d14 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1613,9 +1613,20 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { /// \returns true on success, false on failure. bool typecheckParsedType() { assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr"); - if (!performTypeLocChecking(P.Context, ParsedTypeLoc, - CurDeclContext, false)) + if (ParsedTypeLoc.wasValidated() && !ParsedTypeLoc.isError()) { return true; + } + + const auto ty = swift::performTypeResolution( + ParsedTypeLoc.getTypeRepr(), P.Context, + /*isSILMode=*/false, + /*isSILType=*/false, CurDeclContext->getGenericEnvironmentOfContext(), + CurDeclContext, + /*ProduceDiagnostics=*/false); + ParsedTypeLoc.setType(ty); + if (!ParsedTypeLoc.isError()) { + return true; + } // It doesn't type check as a type, so see if it's a qualifying module name. if (auto *ITR = dyn_cast(ParsedTypeLoc.getTypeRepr())) { @@ -1749,11 +1760,11 @@ static Type defaultTypeLiteralKind(CodeCompletionLiteralKind kind, ASTContext &Ctx) { switch (kind) { case CodeCompletionLiteralKind::BooleanLiteral: - return Ctx.getBoolDecl()->getDeclaredType(); + return Ctx.getBoolDecl()->getDeclaredInterfaceType(); case CodeCompletionLiteralKind::IntegerLiteral: - return Ctx.getIntDecl()->getDeclaredType(); + return Ctx.getIntDecl()->getDeclaredInterfaceType(); case CodeCompletionLiteralKind::StringLiteral: - return Ctx.getStringDecl()->getDeclaredType(); + return Ctx.getStringDecl()->getDeclaredInterfaceType(); case CodeCompletionLiteralKind::ArrayLiteral: return Ctx.getArrayDecl()->getDeclaredType(); case CodeCompletionLiteralKind::DictionaryLiteral: @@ -2594,12 +2605,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { case DefaultArgumentKind::EmptyDictionary: return !includeDefaultArgs; - case DefaultArgumentKind::File: - case DefaultArgumentKind::FilePath: - case DefaultArgumentKind::Line: - case DefaultArgumentKind::Column: - case DefaultArgumentKind::Function: - case DefaultArgumentKind::DSOHandle: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: +#include "swift/AST/MagicIdentifierKinds.def" // Skip parameters that are defaulted to source location or other // caller context information. Users typically don't want to specify // these parameters. @@ -2672,7 +2680,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { const AbstractFunctionDecl *AFD) { if (AFD && AFD->getAttrs().hasAttribute()) Builder.addAnnotatedRethrows(); - else if (AFT->throws()) + else if (AFT->isThrowing()) Builder.addAnnotatedThrows(); } @@ -4032,35 +4040,53 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { /// Add '#file', '#line', et at. void addPoundLiteralCompletions(bool needPound) { - auto addFromProto = [&](StringRef name, CodeCompletionKeywordKind kwKind, - CodeCompletionLiteralKind literalKind) { + auto addFromProto = [&](MagicIdentifierLiteralExpr::Kind magicKind, + Optional literalKind) { + CodeCompletionKeywordKind kwKind; + switch (magicKind) { + case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: + kwKind = CodeCompletionKeywordKind::pound_file; + break; + case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: + // Already handled by above case. + return; +#define MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) \ + case MagicIdentifierLiteralExpr::NAME: \ + kwKind = CodeCompletionKeywordKind::TOKEN; \ + break; +#define MAGIC_IDENTIFIER_DEPRECATED_TOKEN(NAME, TOKEN) +#include "swift/AST/MagicIdentifierKinds.def" + } + + StringRef name = MagicIdentifierLiteralExpr::getKindString(magicKind); if (!needPound) name = name.substr(1); + if (!literalKind) { + // Pointer type + addKeyword(name, "UnsafeRawPointer", kwKind); + return; + } + CodeCompletionResultBuilder builder( Sink, CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::None, {}); - builder.setLiteralKind(literalKind); + builder.setLiteralKind(literalKind.getValue()); builder.setKeywordKind(kwKind); builder.addBaseName(name); - addTypeRelationFromProtocol(builder, literalKind); + addTypeRelationFromProtocol(builder, literalKind.getValue()); }; - addFromProto("#function", CodeCompletionKeywordKind::pound_function, +#define MAGIC_STRING_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + addFromProto(MagicIdentifierLiteralExpr::NAME, \ CodeCompletionLiteralKind::StringLiteral); - addFromProto("#file", CodeCompletionKeywordKind::pound_file, - CodeCompletionLiteralKind::StringLiteral); - if (Ctx.LangOpts.EnableConcisePoundFile) { - addFromProto("#filePath", CodeCompletionKeywordKind::pound_file, - CodeCompletionLiteralKind::StringLiteral); - } - addFromProto("#line", CodeCompletionKeywordKind::pound_line, +#define MAGIC_INT_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + addFromProto(MagicIdentifierLiteralExpr::NAME, \ CodeCompletionLiteralKind::IntegerLiteral); - addFromProto("#column", CodeCompletionKeywordKind::pound_column, - CodeCompletionLiteralKind::IntegerLiteral); - - addKeyword(needPound ? "#dsohandle" : "dsohandle", "UnsafeRawPointer", - CodeCompletionKeywordKind::pound_dsohandle); +#define MAGIC_POINTER_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + addFromProto(MagicIdentifierLiteralExpr::NAME, \ + None); +#include "swift/AST/MagicIdentifierKinds.def" } void addValueLiteralCompletions() { @@ -4115,7 +4141,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addRightBracket(); }); - auto floatType = context.getFloatDecl()->getDeclaredType(); + auto floatType = context.getFloatDecl()->getDeclaredInterfaceType(); addFromProto(LK::ColorLiteral, [&](Builder &builder) { builder.addBaseName("#colorLiteral"); builder.addLeftParen(); @@ -4129,7 +4155,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addRightParen(); }); - auto stringType = context.getStringDecl()->getDeclaredType(); + auto stringType = context.getStringDecl()->getDeclaredInterfaceType(); addFromProto(LK::ImageLiteral, [&](Builder &builder) { builder.addBaseName("#imageLiteral"); builder.addLeftParen(); @@ -6226,7 +6252,9 @@ void CodeCompletionCallbacksImpl::doneParsing() { case CompletionKind::ReturnStmtExpr : { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); - Lookup.setExpectedTypes(getReturnTypeFromContext(CurDeclContext), + SmallVector possibleReturnTypes; + collectPossibleReturnTypesFromContext(CurDeclContext, possibleReturnTypes); + Lookup.setExpectedTypes(possibleReturnTypes, /*isSingleExpressionBody*/ false); Lookup.getValueCompletionsInDeclContext(Loc); break; diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 78b8c4eabf967..26df36591975f 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -295,6 +295,7 @@ bool CompletionInstance::performCachedOperationIfPossible( auto &CI = *CachedCI; auto *oldSF = CI.getCodeCompletionFile(); + assert(oldSF->getBufferID()); auto *oldState = oldSF->getDelayedParserState(); assert(oldState->hasCodeCompletionDelayedDeclState()); @@ -302,12 +303,12 @@ bool CompletionInstance::performCachedOperationIfPossible( auto &SM = CI.getSourceMgr(); auto bufferName = completionBuffer->getBufferIdentifier(); - if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != bufferName) + if (SM.getIdentifierForBuffer(*oldSF->getBufferID()) != bufferName) return false; if (shouldCheckDependencies()) { if (areAnyDependentFilesInvalidated( - CI, *FileSystem, SM.getCodeCompletionBufferID(), + CI, *FileSystem, *oldSF->getBufferID(), DependencyCheckedTimestamp, InMemoryDependencyHash)) return false; DependencyCheckedTimestamp = std::chrono::system_clock::now(); diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 14bb5e1b5ef56..787606e321891 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -42,23 +42,35 @@ using namespace ide; // typeCheckContextAt(DeclContext, SourceLoc) //===----------------------------------------------------------------------===// -namespace { -void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { - // Nothing to type check in module context. - if (DC->isModuleScopeContext()) - return; +void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { + while (isa(DC)) + DC = DC->getParent(); - typeCheckContextImpl(DC->getParent(), Loc); + // Make sure the extension has been bound, in case it is in an inactive #if + // or something weird like that. + { + SmallVector extensions; + for (auto typeCtx = DC->getInnermostTypeContext(); typeCtx != nullptr; + typeCtx = typeCtx->getParent()->getInnermostTypeContext()) { + if (auto *ext = dyn_cast(typeCtx)) + extensions.push_back(ext); + } + while (!extensions.empty()) { + extensions.back()->computeExtendedNominal(); + extensions.pop_back(); + } + } // Type-check this context. switch (DC->getContextKind()) { case DeclContextKind::AbstractClosureExpr: case DeclContextKind::Module: + case DeclContextKind::FileUnit: case DeclContextKind::SerializedLocal: - case DeclContextKind::TopLevelCodeDecl: case DeclContextKind::EnumElementDecl: case DeclContextKind::GenericTypeDecl: case DeclContextKind::SubscriptDecl: + case DeclContextKind::ExtensionDecl: // Nothing to do for these. break; @@ -76,39 +88,22 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { } break; + case DeclContextKind::TopLevelCodeDecl: + swift::typeCheckASTNodeAtLoc(DC, Loc); + break; + case DeclContextKind::AbstractFunctionDecl: { auto *AFD = cast(DC); auto &SM = DC->getASTContext().SourceMgr; auto bodyRange = AFD->getBodySourceRange(); if (SM.rangeContainsTokenLoc(bodyRange, Loc)) { - swift::typeCheckAbstractFunctionBodyAtLoc(AFD, Loc); + swift::typeCheckASTNodeAtLoc(DC, Loc); } else { assert(bodyRange.isInvalid() && "The body should not be parsed if the " "completion happens in the signature"); } break; } - - case DeclContextKind::ExtensionDecl: - // Make sure the extension has been bound, in case it is in an - // inactive #if or something weird like that. - cast(DC)->computeExtendedNominal(); - break; - - case DeclContextKind::FileUnit: - llvm_unreachable("module scope context handled above"); - } -} -} // anonymous namespace - -void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { - while (isa(DC)) - DC = DC->getParent(); - - if (auto *TLCD = dyn_cast(DC)) { - typeCheckTopLevelCodeDecl(TLCD); - } else { - typeCheckContextImpl(DC, Loc); } } @@ -170,7 +165,6 @@ class ExprFinder : public ASTWalker { return {isInterstingRange(S), S}; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } bool walkToTypeReprPre(TypeRepr *T) override { return false; } }; } // anonymous namespace @@ -213,16 +207,30 @@ class CCExprRemover: public ASTWalker, public ExprVisitor } else if (auto tuple = dyn_cast(E->getArg())) { lParenLoc = tuple->getLParenLoc(); rParenLoc = tuple->getRParenLoc(); + + assert((!E->getUnlabeledTrailingClosureIndex().hasValue() || + (tuple->getNumElements() == E->getArgumentLabels().size() && + tuple->getNumElements() == E->getArgumentLabelLocs().size())) && + "CallExpr with trailing closure must have the same number of " + "argument labels"); + assert(tuple->getNumElements() == E->getArgumentLabels().size()); + assert(tuple->getNumElements() == E->getArgumentLabelLocs().size() || + E->getArgumentLabelLocs().size() == 0); + + bool hasArgumentLabelLocs = E->getArgumentLabelLocs().size() > 0; + for (unsigned i = 0, e = tuple->getNumElements(); i != e; ++i) { if (isa(tuple->getElement(i))) { removing = true; continue; } - if (i < E->getUnlabeledTrailingClosureIndex()) { + if (!E->getUnlabeledTrailingClosureIndex().hasValue() || + i < *E->getUnlabeledTrailingClosureIndex()) { // Normal arguments. argLabels.push_back(E->getArgumentLabels()[i]); - argLabelLocs.push_back(E->getArgumentLabelLocs()[i]); + if (hasArgumentLabelLocs) + argLabelLocs.push_back(E->getArgumentLabelLocs()[i]); args.push_back(tuple->getElement(i)); } else { // Trailing closure arguments. @@ -246,7 +254,20 @@ class CCExprRemover: public ASTWalker, public ExprVisitor } std::pair walkToExprPre(Expr *E) override { - return {true, visit(E)}; + if (Removed) + return {false, nullptr}; + E = visit(E); + return {!Removed, E}; + } + + std::pair walkToStmtPre(Stmt *S) override { + if (Removed) + return {false, nullptr}; + return {true, S}; + } + + bool walkToDeclPre(Decl *D) override { + return !Removed; } }; } @@ -258,40 +279,67 @@ bool swift::ide::removeCodeCompletionExpr(ASTContext &Ctx, Expr *&expr) { } //===----------------------------------------------------------------------===// -// getReturnTypeFromContext(DeclContext) +// collectPossibleReturnTypesFromContext(DeclContext, SmallVectorImpl) //===----------------------------------------------------------------------===// -Type swift::ide::getReturnTypeFromContext(const DeclContext *DC) { +void swift::ide::collectPossibleReturnTypesFromContext( + DeclContext *DC, SmallVectorImpl &candidates) { if (auto FD = dyn_cast(DC)) { auto Ty = FD->getInterfaceType(); if (FD->getDeclContext()->isTypeContext()) Ty = FD->getMethodInterfaceType(); - if (auto FT = Ty->getAs()) - return DC->mapTypeIntoContext(FT->getResult()); - } else if (auto ACE = dyn_cast(DC)) { - if (ACE->getType() && !ACE->getType()->hasError()) - return ACE->getResultType(); + if (auto FT = Ty->getAs()) { + candidates.push_back(DC->mapTypeIntoContext(FT->getResult())); + } + } + + if (auto ACE = dyn_cast(DC)) { + // Try type checking the closure signature if it hasn't. + if (!ACE->getType()) + swift::typeCheckASTNodeAtLoc(ACE->getParent(), ACE->getLoc()); + + // Use the type checked type if it has. + if (ACE->getType() && !ACE->getType()->hasError() && + !ACE->getResultType()->hasUnresolvedType()) { + candidates.push_back(ACE->getResultType()); + return; + } + if (auto CE = dyn_cast(ACE)) { if (CE->hasExplicitResultType()) { + // If the closure has a explicit return type, use it. if (auto ty = CE->getExplicitResultType()) { - return ty; + candidates.push_back(ty); + return; + } else { + const auto type = swift::performTypeResolution( + CE->getExplicitResultTypeRepr(), DC->getASTContext(), + /*isSILMode=*/false, /*isSILType=*/false, + DC->getGenericEnvironmentOfContext(), + const_cast(DC), /*diagnostics=*/false); + + if (!type->hasError()) { + candidates.push_back(type); + return; + } } - - auto typeLoc = TypeLoc{CE->getExplicitResultTypeRepr()}; - if (swift::performTypeLocChecking(DC->getASTContext(), - typeLoc, - /*isSILMode*/ false, - /*isSILType*/ false, - DC->getGenericEnvironmentOfContext(), - const_cast(DC), - /*diagnostics*/ false)) { - return Type(); + } else { + // Otherwise, check the context type of the closure. + ExprContextInfo closureCtxInfo(CE->getParent(), CE); + for (auto closureTy : closureCtxInfo.getPossibleTypes()) { + if (auto funcTy = closureTy->getAs()) + candidates.push_back(funcTy->getResult()); } - return typeLoc.getType(); + if (!candidates.empty()) + return; } } + + // Even if the type checked type has unresolved types, it's better than + // nothing. + if (ACE->getType() && !ACE->getType()->hasError()) + candidates.push_back(ACE->getResultType()); } - return Type(); } //===----------------------------------------------------------------------===// @@ -621,11 +669,9 @@ static bool collectPossibleCalleesForUnresolvedMember( SmallVectorImpl &candidates) { auto currModule = DC.getParentModule(); - // Get the context of the expression itself. - ExprContextInfo contextInfo(&DC, unresolvedMemberExpr); - for (auto expectedTy : contextInfo.getPossibleTypes()) { + auto collectMembers = [&](Type expectedTy) { if (!expectedTy->mayHaveMembers()) - continue; + return; SmallVector members; collectPossibleCalleesByQualifiedLookup(DC, MetatypeType::get(expectedTy), unresolvedMemberExpr->getName(), @@ -635,6 +681,16 @@ static bool collectPossibleCalleesForUnresolvedMember( member.Decl)) candidates.push_back(member); } + }; + + // Get the context of the expression itself. + ExprContextInfo contextInfo(&DC, unresolvedMemberExpr); + for (auto expectedTy : contextInfo.getPossibleTypes()) { + collectMembers(expectedTy); + // If this is an optional type, let's also check its base type. + if (auto baseTy = expectedTy->getOptionalObjectType()) { + collectMembers(baseTy->lookThroughAllOptionalTypes()); + } } return !candidates.empty(); } @@ -739,10 +795,7 @@ class ExprContextAnalyzer { auto Params = typeAndDecl.Type->getParams(); ParameterList *paramList = nullptr; if (auto VD = typeAndDecl.Decl) { - if (auto FD = dyn_cast(VD)) - paramList = FD->getParameters(); - else if (auto SD = dyn_cast(VD)) - paramList = SD->getIndices(); + paramList = getParameterList(VD); if (paramList && paramList->size() != Params.size()) paramList = nullptr; } @@ -763,6 +816,8 @@ class ExprContextAnalyzer { auto argTy = ty; if (paramType.isInOut()) argTy = InOutType::get(argTy); + else if (paramType.isAutoClosure() && argTy->is()) + argTy = argTy->castTo()->getResult(); if (seenTypes.insert(argTy.getPointer()).second) recordPossibleType(argTy); } @@ -905,7 +960,10 @@ class ExprContextAnalyzer { auto *CE = cast(Parent); assert(isSingleExpressionBodyForCodeCompletion(CE->getBody())); singleExpressionBody = true; - recordPossibleType(getReturnTypeFromContext(CE)); + SmallVector candidates; + collectPossibleReturnTypesFromContext(CE, candidates); + for (auto ty : candidates) + recordPossibleType(ty); break; } default: @@ -915,9 +973,13 @@ class ExprContextAnalyzer { void analyzeStmt(Stmt *Parent) { switch (Parent->getKind()) { - case StmtKind::Return: - recordPossibleType(getReturnTypeFromContext(DC)); + case StmtKind::Return: { + SmallVector candidates; + collectPossibleReturnTypesFromContext(DC, candidates); + for (auto ty : candidates) + recordPossibleType(ty); break; + } case StmtKind::ForEach: if (auto SEQ = cast(Parent)->getSequence()) { if (containsTarget(SEQ)) { @@ -980,7 +1042,10 @@ class ExprContextAnalyzer { if (auto *FD = dyn_cast(D)) { assert(isSingleExpressionBodyForCodeCompletion(FD->getBody())); singleExpressionBody = true; - recordPossibleType(getReturnTypeFromContext(FD)); + SmallVector candidates; + collectPossibleReturnTypesFromContext(DC, candidates); + for (auto ty : candidates) + recordPossibleType(ty); break; } llvm_unreachable("Unhandled decl kind."); diff --git a/lib/IDE/ExprContextAnalysis.h b/lib/IDE/ExprContextAnalysis.h index d6b039693fb70..1e0b749ac065c 100644 --- a/lib/IDE/ExprContextAnalysis.h +++ b/lib/IDE/ExprContextAnalysis.h @@ -41,9 +41,10 @@ Expr *findParsedExpr(const DeclContext *DC, SourceRange TargetRange); /// position. bool removeCodeCompletionExpr(ASTContext &Ctx, Expr *&expr); -/// Returns expected return type of the given decl context. +/// Collects possible expected return types of the given decl context. /// \p DC should be an \c AbstractFunctionDecl or an \c AbstractClosureExpr. -Type getReturnTypeFromContext(const DeclContext *DC); +void collectPossibleReturnTypesFromContext(DeclContext *DC, + SmallVectorImpl &candidates); struct FunctionTypeAndDecl { AnyFunctionType *Type; diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 64a78e7ec12b3..69b9fc92f4b7a 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -485,7 +485,7 @@ class RangeWalker: protected ASTWalker { bool SafeToAskForGenerics = !isa(D) && !isa(D); if (SafeToAskForGenerics) { - if (auto *GP = GC->getGenericParams()) { + if (auto *GP = GC->getParsedGenericParams()) { if (!handleAngles(GP->getLAngleLoc(), GP->getRAngleLoc(), ContextLoc)) return Stop; } @@ -501,18 +501,14 @@ class RangeWalker: protected ASTWalker { } else if (auto *VD = dyn_cast(D)) { if (!handleBraces(VD->getBracesRange(), VD->getNameLoc())) return Stop; - } else if (auto *AFD = dyn_cast(D)) { - if (auto *PL = AFD->getParameters()) { - if (!handleParens(PL->getLParenLoc(), PL->getRParenLoc(), ContextLoc)) + } else if (isa(D) || isa(D)) { + if (isa(D)) { + if (!handleBraces(cast(D)->getBracesRange(), ContextLoc)) return Stop; } - } else if (auto *SD = dyn_cast(D)) { - if (!handleBraces(SD->getBracesRange(), ContextLoc)) + auto *PL = getParameterList(cast(D)); + if (!handleParens(PL->getLParenLoc(), PL->getRParenLoc(), ContextLoc)) return Stop; - if (auto *PL = SD->getIndices()) { - if (!handleParens(PL->getLParenLoc(), PL->getRParenLoc(), ContextLoc)) - return Stop; - } } else if (auto *PGD = dyn_cast(D)) { SourceRange Braces(PGD->getLBraceLoc(), PGD->getRBraceLoc()); if (!handleBraces(Braces, ContextLoc)) @@ -1587,7 +1583,9 @@ class FormatWalker : public ASTWalker { return Ctx; if (auto Ctx = getIndentContextFrom(AFD->getParameters(), ContextLoc)) return Ctx; - if (auto Ctx = getIndentContextFrom(AFD->getGenericParams(), ContextLoc, D)) + if (auto Ctx = getIndentContextFrom(AFD->getParsedGenericParams(), ContextLoc, D)) + return Ctx; + if (auto Ctx = getIndentContextFrom(AFD->getTrailingWhereClause(), ContextLoc, D)) return Ctx; if (TrailingTarget) @@ -1602,7 +1600,7 @@ class FormatWalker : public ASTWalker { return Ctx; if (auto Ctx = getIndentContextFromBraces(NTD->getBraces(), ContextLoc, NTD)) return Ctx; - if (auto Ctx = getIndentContextFrom(NTD->getGenericParams(), ContextLoc, D)) + if (auto Ctx = getIndentContextFrom(NTD->getParsedGenericParams(), ContextLoc, D)) return Ctx; if (auto Ctx = getIndentContextFrom(NTD->getTrailingWhereClause(), ContextLoc, D)) return Ctx; @@ -1660,7 +1658,9 @@ class FormatWalker : public ASTWalker { return Ctx; if (auto Ctx = getIndentContextFrom(SD->getIndices(), ContextLoc)) return Ctx; - if (auto Ctx = getIndentContextFrom(SD->getGenericParams(), ContextLoc, D)) + if (auto Ctx = getIndentContextFrom(SD->getParsedGenericParams(), ContextLoc, D)) + return Ctx; + if (auto Ctx = getIndentContextFrom(SD->getTrailingWhereClause(), ContextLoc, D)) return Ctx; if (TrailingTarget) @@ -1756,7 +1756,11 @@ class FormatWalker : public ASTWalker { if (auto *TAD = dyn_cast(D)) { SourceLoc ContextLoc = TAD->getStartLoc(); - if (auto Ctx = getIndentContextFrom(TAD->getGenericParams(), ContextLoc, + if (auto Ctx = getIndentContextFrom(TAD->getParsedGenericParams(), ContextLoc, + D)) { + return Ctx; + } + if (auto Ctx = getIndentContextFrom(TAD->getTrailingWhereClause(), ContextLoc, D)) { return Ctx; } @@ -1878,11 +1882,6 @@ class FormatWalker : public ASTWalker { return Ctx; } - SourceRange TrailingRange = GP->getTrailingWhereClauseSourceRange(); - if (auto Ctx = getIndentContextFromWhereClause(GP->getRequirements(), - TrailingRange, ContextLoc, - WalkableParent)) - return Ctx; return None; } diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index c928cb3dd6824..b34975836c92f 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -471,7 +471,11 @@ struct SynthesizedExtensionAnalyzer::Implementation { // We want to visit the protocols of any normal conformances we see, but // we have to avoid doing this to self-conformances or we can end up with // a cycle. Otherwise this is cycle-proof on valid code. + // We also want to ignore inherited conformances. Members from these will + // be included in the class they were inherited from. auto addConformance = [&](ProtocolConformance *Conf) { + if (isa(Conf)) + return; auto RootConf = Conf->getRootConformance(); if (isa(RootConf)) Unhandled.push_back(RootConf->getProtocol()); @@ -480,10 +484,6 @@ struct SynthesizedExtensionAnalyzer::Implementation { for (auto *Conf : Target->getLocalConformances()) { addConformance(Conf); } - if (auto *CD = dyn_cast(Target)) { - if (auto Super = CD->getSuperclassDecl()) - Unhandled.push_back(Super); - } while (!Unhandled.empty()) { NominalTypeDecl* Back = Unhandled.back(); Unhandled.pop_back(); @@ -493,10 +493,6 @@ struct SynthesizedExtensionAnalyzer::Implementation { for (auto *Conf : Back->getLocalConformances()) { addConformance(Conf); } - if (auto *CD = dyn_cast(Back)) { - if (auto Super = CD->getSuperclass()) - Unhandled.push_back(Super->getAnyNominal()); - } } // Merge with actual extensions. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 7bd3af342739a..8c6bdcc01143e 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -1846,11 +1846,7 @@ findConcatenatedExpressions(ResolvedRangeInfo Info, ASTContext &Ctx) { switch (Info.Kind) { case RangeKind::SingleExpression: - // FIXME: the range info kind should imply non-empty list. - if (!Info.ContainedNodes.empty()) - E = Info.ContainedNodes[0].get(); - else - return nullptr; + E = Info.ContainedNodes[0].get(); break; case RangeKind::PartOfExpression: E = Info.CommonExprParent; diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index d52e8c43fd368..4b16a71f96a2a 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -140,12 +140,9 @@ bool SemaAnnotator::walkToDeclPre(Decl *D) { return false; }; - if (auto AF = dyn_cast(VD)) { - if (ReportParamList(AF->getParameters())) - return false; - } - if (auto SD = dyn_cast(VD)) { - if (ReportParamList(SD->getIndices())) + if (isa(VD) || isa(VD)) { + auto ParamList = getParameterList(VD); + if (ReportParamList(ParamList)) return false; } } else if (auto *ED = dyn_cast(D)) { @@ -415,6 +412,7 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::OptionalForce: case KeyPathExpr::Component::Kind::Identity: + case KeyPathExpr::Component::Kind::DictionaryKey: break; } } diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index a08719d75af83..b372b1cba3e39 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -468,16 +468,6 @@ Expr *NameMatcher::walkToExprPost(Expr *E) { return E; } -bool NameMatcher::walkToTypeLocPre(TypeLoc &TL) { - if (isDone() || shouldSkip(TL.getSourceRange())) - return false; - return true; -} - -bool NameMatcher::walkToTypeLocPost(TypeLoc &TL) { - return !isDone(); -} - bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isDone() || shouldSkip(T->getSourceRange())) return false; diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index 1e73fff73d49b..7cfe61b987e86 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -69,4 +69,5 @@ target_link_libraries(swiftIRGen PRIVATE swiftLLVMPasses swiftSIL swiftSILGen - swiftSILOptimizer) + swiftSILOptimizer + swiftTBDGen) diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 168f7cc0eea64..441eabf62a24b 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -103,6 +103,10 @@ template class ClassMetadataVisitor /// of a generic-layout class. void noteEndOfFieldOffsets(ClassDecl *whichClass) {} + /// Notes the existence of a formally virtual method that has been elided from the + /// reified vtable because it has no overrides. + void noteNonoverriddenMethod(SILDeclRef method) {} + private: /// Add fields associated with the given class and its bases. void addClassMembers(ClassDecl *theClass) { @@ -171,10 +175,13 @@ template class ClassMetadataVisitor friend SILVTableVisitor; void addMethod(SILDeclRef declRef) { // Does this method require a reified runtime vtable entry? - if (methodRequiresReifiedVTableEntry(IGM, VTable, declRef)) { + if (!VTable || methodRequiresReifiedVTableEntry(IGM, VTable, declRef)) { asImpl().addReifiedVTableEntry(declRef); + } else { + asImpl().noteNonoverriddenMethod(declRef); } } + void addFieldEntries(Decl *field) { if (auto var = dyn_cast(field)) { diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index d97c0dd3f8e29..fa1c53f73a465 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -410,7 +410,7 @@ if (Builtin.ID == BuiltinValueKind::id) { \ auto error = args.claimNext(); auto errorBuffer = IGF.getErrorResultSlot( SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() - ->getDeclaredType() + ->getDeclaredInterfaceType() ->getCanonicalType())); IGF.Builder.CreateStore(error, errorBuffer); diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 2098294809116..912f0e3ad6131 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -2057,7 +2057,7 @@ namespace { : Twine()), IGM.getPointerAlignment(), /*constant*/ true, - llvm::GlobalVariable::PrivateLinkage); + llvm::GlobalVariable::InternalLinkage); switch (IGM.TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: @@ -2512,18 +2512,26 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, // Find the vtable entry we're interested in. auto methodInfo = IGF.IGM.getClassMetadataLayout(classDecl).getMethodInfo(IGF, method); - auto offset = methodInfo.getOffset(); - - auto slot = IGF.emitAddressAtOffset(metadata, offset, - signature.getType()->getPointerTo(), - IGF.IGM.getPointerAlignment()); - auto fnPtr = IGF.emitInvariantLoad(slot); - - auto &schema = IGF.getOptions().PointerAuth.SwiftClassMethods; - auto authInfo = - PointerAuthInfo::emit(IGF, schema, slot.getAddress(), method); - - return FunctionPointer(fnPtr, authInfo, signature); + switch (methodInfo.getKind()) { + case ClassMetadataLayout::MethodInfo::Kind::Offset: { + auto offset = methodInfo.getOffsett(); + + auto slot = IGF.emitAddressAtOffset(metadata, offset, + signature.getType()->getPointerTo(), + IGF.IGM.getPointerAlignment()); + auto fnPtr = IGF.emitInvariantLoad(slot); + auto &schema = IGF.getOptions().PointerAuth.SwiftClassMethods; + auto authInfo = + PointerAuthInfo::emit(IGF, schema, slot.getAddress(), method); + return FunctionPointer(fnPtr, authInfo, signature); + } + case ClassMetadataLayout::MethodInfo::Kind::DirectImpl: { + auto fnPtr = llvm::ConstantExpr::getBitCast(methodInfo.getDirectImpl(), + signature.getType()->getPointerTo()); + return FunctionPointer::forDirect(fnPtr, signature); + } + } + } FunctionPointer diff --git a/lib/IRGen/GenConstant.cpp b/lib/IRGen/GenConstant.cpp index 071d49f82603b..0d6e021e8327f 100644 --- a/lib/IRGen/GenConstant.cpp +++ b/lib/IRGen/GenConstant.cpp @@ -102,14 +102,6 @@ llvm::Constant *irgen::emitAddrOfConstantString(IRGenModule &IGM, case StringLiteralInst::Encoding::UTF8: return IGM.getAddrOfGlobalString(SLI->getValue()); - case StringLiteralInst::Encoding::UTF16: { - // This is always a GEP of a GlobalVariable with a nul terminator. - auto addr = IGM.getAddrOfGlobalUTF16String(SLI->getValue()); - - // Cast to Builtin.RawPointer. - return llvm::ConstantExpr::getBitCast(addr, IGM.Int8PtrTy); - } - case StringLiteralInst::Encoding::ObjCSelector: llvm_unreachable("cannot get the address of an Objective-C selector"); } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 6367d5229a311..dffa6c581e48d 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1030,7 +1030,8 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) { return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping; } -void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { +void IRGenerator::emitGlobalTopLevel( + const std::vector &linkerDirectives) { // Generate order numbers for the functions in the SIL module that // correspond to definitions in the LLVM module. unsigned nextOrderNumber = 0; @@ -1050,10 +1051,8 @@ void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { CurrentIGMPtr IGM = getGenModule(wt.getProtocol()->getDeclContext()); ensureRelativeSymbolCollocation(wt); } - if (linkerDirectives) { - for (auto &entry: *linkerDirectives) { - createLinkerDirectiveVariable(*PrimaryIGM, entry.getKey()); - } + for (auto &directive: linkerDirectives) { + createLinkerDirectiveVariable(*PrimaryIGM, directive); } for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) { Decl *decl = v.getDecl(); @@ -1119,6 +1118,8 @@ void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { IRGenModule *IGM = Iter.second; IGM->finishEmitAfterTopLevel(); } + + emitEntryPointInfo(); } void IRGenModule::finishEmitAfterTopLevel() { @@ -1427,7 +1428,22 @@ void IRGenerator::noteUseOfCanonicalSpecializedMetadataAccessor( } } +static bool typeKindCanBePrespecialized(TypeKind theKind) { + switch (theKind) { + case TypeKind::Struct: + case TypeKind::BoundGenericStruct: + case TypeKind::Enum: + case TypeKind::BoundGenericEnum: + case TypeKind::Class: + case TypeKind::BoundGenericClass: + return true; + default: + return false; + } +} + void IRGenerator::noteUseOfSpecializedGenericTypeMetadata(CanType type) { + assert(typeKindCanBePrespecialized(type->getKind())); auto key = type->getAnyNominal(); assert(key); assert(key->isGenericContext()); @@ -1791,6 +1807,44 @@ void IRGenModule::emitVTableStubs() { } } +static std::string getEntryPointSection(IRGenModule &IGM) { + std::string sectionName; + switch (IGM.TargetInfo.OutputObjectFormat) { + case llvm::Triple::UnknownObjectFormat: + llvm_unreachable("Don't know how to emit field records table for " + "the selected object format."); + case llvm::Triple::MachO: + sectionName = "__TEXT, __swift5_entry, regular, no_dead_strip"; + break; + case llvm::Triple::ELF: + case llvm::Triple::Wasm: + sectionName = "swift5_entry"; + break; + case llvm::Triple::XCOFF: + case llvm::Triple::COFF: + sectionName = ".sw5entr$B"; + break; + } + return sectionName; +} + +void IRGenerator::emitEntryPointInfo() { + SILFunction *entrypoint = nullptr; + if (!(entrypoint = SIL.lookUpFunction(SWIFT_ENTRY_POINT_FUNCTION))) { + return; + } + auto &IGM = *getGenModule(entrypoint); + ConstantInitBuilder builder(IGM); + auto entrypointInfo = builder.beginStruct(); + entrypointInfo.addRelativeAddress( + IGM.getAddrOfSILFunction(entrypoint, NotForDefinition)); + auto var = entrypointInfo.finishAndCreateGlobal( + "\x01l_entry_point", Alignment(4), + /*isConstant*/ true, llvm::GlobalValue::PrivateLinkage); + var->setSection(getEntryPointSection(IGM)); + IGM.addUsedGlobal(var); +} + static IRLinkage getIRLinkage(const UniversalLinkageInfo &info, SILLinkage linkage, ForDefinition_t isDefinition, bool isWeakImported, @@ -1894,11 +1948,7 @@ void irgen::updateLinkageForDefinition(IRGenModule &IGM, // Everything externally visible is considered used in Swift. // That mostly means we need to be good at not marking things external. - // - // Exclude "main", because it should naturally be used, and because adding it - // to llvm.used leaves a dangling use when the REPL attempts to discard - // intermediate mains. - if (LinkInfo::isUsed(IRL) && global->getName() != SWIFT_ENTRY_POINT_FUNCTION) + if (LinkInfo::isUsed(IRL)) IGM.addUsedGlobal(global); } @@ -1989,11 +2039,7 @@ llvm::Function *irgen::createFunction(IRGenModule &IGM, // Everything externally visible is considered used in Swift. // That mostly means we need to be good at not marking things external. - // - // Exclude "main", because it should naturally be used, and because adding it - // to llvm.used leaves a dangling use when the REPL attempts to discard - // intermediate mains. - if (linkInfo.isUsed() && name != SWIFT_ENTRY_POINT_FUNCTION) { + if (linkInfo.isUsed()) { IGM.addUsedGlobal(fn); } @@ -2097,9 +2143,9 @@ swift::irgen::createLinkerDirectiveVariable(IRGenModule &IGM, StringRef name) { } void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var) { - // Add an operand to llvm.asan.globals blacklisting this global variable. + // Add an operand to llvm.asan.globals denylisting this global variable. llvm::Metadata *metadata[] = { - // The global variable to blacklist. + // The global variable to denylist. llvm::ConstantAsMetadata::get(var), // Source location. Optional, unnecessary here. @@ -2112,7 +2158,7 @@ void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariabl llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( llvm::Type::getInt1Ty(IGM.Module.getContext()), false)), - // Whether the global is blacklisted. + // Whether the global is denylisted. llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( llvm::Type::getInt1Ty(IGM.Module.getContext()), true))}; @@ -3748,6 +3794,22 @@ IRGenModule::getAddrOfTypeMetadataLazyCacheVariable(CanType type) { return variable; } +llvm::Constant * +IRGenModule::getAddrOfNoncanonicalSpecializedGenericTypeMetadataCacheVariable(CanType type) { + assert(!type->hasArchetype() && !type->hasTypeParameter()); + LinkEntity entity = LinkEntity::forNoncanonicalSpecializedGenericTypeMetadataCacheVariable(type); + if (auto &entry = GlobalVars[entity]) { + return entry; + } + auto variable = + getAddrOfLLVMVariable(entity, ForDefinition, DebugTypeInfo()); + + cast(variable)->setInitializer( + llvm::ConstantPointerNull::get(TypeMetadataPtrTy)); + + return variable; +} + /// Get or create a type metadata cache variable. These are an /// implementation detail of type metadata access functions. llvm::Constant * @@ -3829,6 +3891,9 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, llvm::StringRef section) { assert(init); + auto isPrespecialized = concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext(); + if (isPattern) { assert(isConstant && "Type metadata patterns must be constant"); auto addr = getAddrOfTypeMetadataPattern(concreteType->getAnyNominal(), @@ -3856,7 +3921,13 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, adjustmentIndex = MetadataAdjustmentIndex::ValueType; } - auto entity = LinkEntity::forTypeMetadata(concreteType, addrKind); + auto entity = + (isPrespecialized && + !irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable( + *this, concreteType)) + ? LinkEntity::forNoncanonicalSpecializedGenericTypeMetadata( + concreteType) + : LinkEntity::forTypeMetadata(concreteType, addrKind); auto DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), entity.getDefaultDeclarationType(*this)->getPointerTo(), @@ -3880,9 +3951,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, // 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())) + if ((nominal && requiresForeignTypeMetadata(nominal)) || isPrespecialized) return var; // For concrete metadata, declare the alias to its address point. @@ -3904,13 +3973,18 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, } /// Fetch the declaration of the (possibly uninitialized) metadata for a type. -llvm::Constant *IRGenModule::getAddrOfTypeMetadata(CanType concreteType) { - return getAddrOfTypeMetadata(concreteType, - SymbolReferenceKind::Absolute).getDirectValue(); +llvm::Constant * +IRGenModule::getAddrOfTypeMetadata(CanType concreteType, + TypeMetadataCanonicality canonicality) { + return getAddrOfTypeMetadata(concreteType, SymbolReferenceKind::Absolute, + canonicality) + .getDirectValue(); } -ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, - SymbolReferenceKind refKind) { +ConstantReference +IRGenModule::getAddrOfTypeMetadata(CanType concreteType, + SymbolReferenceKind refKind, + TypeMetadataCanonicality canonicality) { assert(!isa(concreteType)); auto nominal = concreteType->getAnyNominal(); @@ -3964,12 +4038,20 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, Optional entity; DebugTypeInfo DbgTy; - if (fullMetadata) { - entity = LinkEntity::forTypeMetadata(concreteType, - TypeMetadataAddress::FullMetadata); - } else { - entity = LinkEntity::forTypeMetadata(concreteType, - TypeMetadataAddress::AddressPoint); + switch (canonicality) { + case TypeMetadataCanonicality::Canonical: + if (fullMetadata) { + entity = LinkEntity::forTypeMetadata(concreteType, + TypeMetadataAddress::FullMetadata); + } else { + entity = LinkEntity::forTypeMetadata(concreteType, + TypeMetadataAddress::AddressPoint); + } + break; + case TypeMetadataCanonicality::Noncanonical: + entity = + LinkEntity::forNoncanonicalSpecializedGenericTypeMetadata(concreteType); + break; } DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), defaultVarTy->getPointerTo(), Size(0), diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 4986ca5460225..9d28264fec78f 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -263,6 +263,55 @@ static Flags getMethodDescriptorFlags(ValueDecl *fn) { return Flags(kind).withIsInstance(!fn->isStatic()); } +static void buildMethodDescriptorFields(IRGenModule &IGM, + const SILVTable *VTable, + SILDeclRef fn, + ConstantStructBuilder &descriptor) { + auto *func = cast(fn.getDecl()); + // Classify the method. + using Flags = MethodDescriptorFlags; + auto flags = getMethodDescriptorFlags(func); + + // Remember if the declaration was dynamic. + if (func->shouldUseObjCDispatch()) + flags = flags.withIsDynamic(true); + + // Include the pointer-auth discriminator. + if (auto &schema = IGM.getOptions().PointerAuth.SwiftClassMethods) { + auto discriminator = + PointerAuthInfo::getOtherDiscriminator(IGM, schema, fn); + flags = flags.withExtraDiscriminator(discriminator->getZExtValue()); + } + + // TODO: final? open? + descriptor.addInt(IGM.Int32Ty, flags.getIntValue()); + + if (auto entry = VTable->getEntry(IGM.getSILModule(), fn)) { + assert(entry->getKind() == SILVTable::Entry::Kind::Normal); + auto *implFn = IGM.getAddrOfSILFunction(entry->getImplementation(), + NotForDefinition); + descriptor.addRelativeAddress(implFn); + } else { + // The method is removed by dead method elimination. + // It should be never called. We add a pointer to an error function. + descriptor.addRelativeAddressOrNull(nullptr); + } +} + +void IRGenModule::emitNonoverriddenMethodDescriptor(const SILVTable *VTable, + SILDeclRef declRef) { + + ConstantInitBuilder ib(*this); + ConstantStructBuilder sb(ib.beginStruct(MethodDescriptorStructTy)); + + buildMethodDescriptorFields(*this, VTable, declRef, sb); + + auto init = sb.finishAndCreateFuture(); + + auto entity = LinkEntity::forMethodDescriptor(declRef); + getAddrOfLLVMVariable(entity, init, DebugTypeInfo()); +} + namespace { template class ContextDescriptorBuilderBase { @@ -612,7 +661,7 @@ namespace { ProtocolDescriptorBuilder(IRGenModule &IGM, ProtocolDecl *Proto, SILDefaultWitnessTable *defaultWitnesses) : super(IGM), Proto(Proto), DefaultWitnesses(defaultWitnesses), - Resilient(IGM.isResilient(Proto, ResilienceExpansion::Minimal)) {} + Resilient(IGM.getSwiftModule()->isResilient()) {} void layout() { super::layout(); @@ -1416,6 +1465,7 @@ namespace { SILVTable *VTable; bool Resilient; + bool HasNonoverriddenMethods = false; SmallVector VTableEntries; SmallVector, 8> OverrideTableEntries; @@ -1440,9 +1490,9 @@ namespace { } void addMethod(SILDeclRef fn) { - if (methodRequiresReifiedVTableEntry(IGM, VTable, fn)) { + if (!VTable || methodRequiresReifiedVTableEntry(IGM, VTable, fn)) { VTableEntries.push_back(fn); - } else if (getType()->getEffectiveAccess() >= AccessLevel::Public) { + } else { // Emit a stub method descriptor and lookup function for nonoverridden // methods so that resilient code sequences can still use them. emitNonoverriddenMethod(fn); @@ -1539,14 +1589,15 @@ namespace { } ); - if (VTableEntries.empty()) - return; - // Only emit a method lookup function if the class is resilient - // and has a non-empty vtable. - if (IGM.hasResilientMetadata(getType(), ResilienceExpansion::Minimal)) + // and has a non-empty vtable, as well as no elided methods. + if (IGM.hasResilientMetadata(getType(), ResilienceExpansion::Minimal) + && (HasNonoverriddenMethods || !VTableEntries.empty())) IGM.emitMethodLookupFunction(getType()); + if (VTableEntries.empty()) + return; + auto offset = MetadataLayout->hasResilientSuperclass() ? MetadataLayout->getRelativeVTableOffset() : MetadataLayout->getStaticVTableOffset(); @@ -1558,47 +1609,19 @@ namespace { } void emitMethodDescriptor(SILDeclRef fn) { + // Define the method descriptor to point to the current position in the - // nominal type descriptor. + // nominal type descriptor, if it has a well-defined symbol name. IGM.defineMethodDescriptor(fn, Type, B.getAddrOfCurrentPosition(IGM.MethodDescriptorStructTy)); // Actually build the descriptor. - auto *func = cast(fn.getDecl()); auto descriptor = B.beginStruct(IGM.MethodDescriptorStructTy); - - // Classify the method. - using Flags = MethodDescriptorFlags; - auto flags = getMethodDescriptorFlags(func); - - // Remember if the declaration was dynamic. - if (func->shouldUseObjCDispatch()) - flags = flags.withIsDynamic(true); - - // Include the pointer-auth discriminator. - if (auto &schema = IGM.getOptions().PointerAuth.SwiftClassMethods) { - auto discriminator = - PointerAuthInfo::getOtherDiscriminator(IGM, schema, fn); - flags = flags.withExtraDiscriminator(discriminator->getZExtValue()); - } - - // TODO: final? open? - descriptor.addInt(IGM.Int32Ty, flags.getIntValue()); - - if (auto entry = VTable->getEntry(IGM.getSILModule(), fn)) { - assert(entry->getKind() == SILVTable::Entry::Kind::Normal); - auto *implFn = IGM.getAddrOfSILFunction(entry->getImplementation(), - NotForDefinition); - descriptor.addRelativeAddress(implFn); - } else { - // The method is removed by dead method elimination. - // It should be never called. We add a pointer to an error function. - descriptor.addRelativeAddressOrNull(nullptr); - } - + buildMethodDescriptorFields(IGM, VTable, fn, descriptor); descriptor.finishAndAddTo(B); // Emit method dispatch thunk if the class is resilient. + auto *func = cast(fn.getDecl()); if (Resilient && func->getEffectiveAccess() >= AccessLevel::Public) { IGM.emitDispatchThunk(fn); @@ -1606,9 +1629,26 @@ namespace { } void emitNonoverriddenMethod(SILDeclRef fn) { - // TODO: Emit a freestanding method descriptor structure, and a method - // lookup function, to present the ABI of an overridable method even - // though the method has no real overrides currently. + // TODO: Derivative functions do not distinguish themselves in the mangled + // names of method descriptor symbols yet, causing symbol name collisions. + if (fn.derivativeFunctionIdentifier) + return; + + HasNonoverriddenMethods = true; + // Although this method is non-overridden and therefore left out of the + // vtable, we still need to maintain the ABI of a potentially-overridden + // method for external clients. + + // Emit method dispatch thunk. + if (hasPublicVisibility(fn.getLinkage(NotForDefinition))) { + IGM.emitDispatchThunk(fn); + } + + // Emit a freestanding method descriptor structure. This doesn't have to + // exist in the table in the class's context descriptor since it isn't + // in the vtable, but external clients need to be able to link against the + // symbol. + IGM.emitNonoverriddenMethodDescriptor(VTable, fn); } void addOverrideTable() { @@ -1977,8 +2017,9 @@ void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, *type.getClassOrBoundGenericClass()); break; default: - llvm_unreachable("Cannot statically specialize types of kind other than " - "struct and enum."); + llvm_unreachable( + "Cannot statically specialize metadata for generic types of" + "kind other than struct, enum, and class."); } } @@ -3476,7 +3517,9 @@ namespace { MetadataTrailingFlags flags = super::getTrailingFlags(); flags.setIsStaticSpecialization(true); - flags.setIsCanonicalStaticSpecialization(true); + flags.setIsCanonicalStaticSpecialization( + irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable( + IGM, type)); return flags; } @@ -5115,28 +5158,61 @@ void IRGenModule::emitOpaqueTypeDecl(OpaqueTypeDecl *D) { bool irgen::methodRequiresReifiedVTableEntry(IRGenModule &IGM, const SILVTable *vtable, SILDeclRef method) { - auto entry = vtable->getEntry(IGM.getSILModule(), method); + Optional entry + = vtable->getEntry(IGM.getSILModule(), method); + LLVM_DEBUG(llvm::dbgs() << "looking at vtable:\n"; + vtable->print(llvm::dbgs())); if (!entry) { + LLVM_DEBUG(llvm::dbgs() << "vtable entry in " + << vtable->getClass()->getName() + << " for "; + method.print(llvm::dbgs()); + llvm::dbgs() << " is not available\n"); return true; } + LLVM_DEBUG(llvm::dbgs() << "entry: "; + entry->print(llvm::dbgs()); + llvm::dbgs() << "\n"); // We may be able to elide the vtable entry, ABI permitting, if it's not // overridden. if (!entry->isNonOverridden()) { + LLVM_DEBUG(llvm::dbgs() << "vtable entry in " + << vtable->getClass()->getName() + << " for "; + method.print(llvm::dbgs()); + llvm::dbgs() << " is overridden\n"); return true; } - // Does the ABI require a vtable entry to exist? If the class is public, + // Does the ABI require a vtable entry to exist? If the class the vtable + // entry originates from is public, // and it's either marked fragile or part of a non-resilient module, then // other modules will directly address vtable offsets and we can't remove // vtable entries. - if (vtable->getClass()->getEffectiveAccess() >= AccessLevel::Public) { - // TODO: Check whether we use a resilient ABI to access this - // class's methods. We can drop unnecessary vtable entries if we do; - // otherwise fixed vtable offsets are part of the ABI. - return true; + auto originatingClass = + cast(method.getOverriddenVTableEntry().getDecl()->getDeclContext()); + + if (originatingClass->getEffectiveAccess() >= AccessLevel::Public) { + // If the class is public, + // and it's either marked fragile or part of a non-resilient module, then + // other modules will directly address vtable offsets and we can't remove + // vtable entries. + if (!originatingClass->isResilient()) { + LLVM_DEBUG(llvm::dbgs() << "vtable entry in " + << vtable->getClass()->getName() + << " for "; + method.print(llvm::dbgs()); + llvm::dbgs() << " originates from a public fragile class\n"); + return true; + } } // Otherwise, we can leave this method out of the runtime vtable. + LLVM_DEBUG(llvm::dbgs() << "vtable entry in " + << vtable->getClass()->getName() + << " for "; + method.print(llvm::dbgs()); + llvm::dbgs() << " can be elided\n"); return false; } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 3e7d81c2ca5fd..2795553485a5a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1924,11 +1924,16 @@ namespace { llvm::ArrayType::get(IGM.Int8PtrTy, swift::NumGenericMetadataPrivateDataWords); auto privateDataInit = llvm::Constant::getNullValue(privateDataTy); + + IRGenMangler mangler; + auto symbolName = + mangler.mangleProtocolConformanceInstantiationCache(Conformance); + auto privateData = new llvm::GlobalVariable(IGM.Module, privateDataTy, /*constant*/ false, llvm::GlobalVariable::InternalLinkage, - privateDataInit, ""); + privateDataInit, symbolName); B.addRelativeAddress(privateData); } } diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 9ad5cc54ac19b..6790794422dd0 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -780,8 +780,8 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { auto *PD = dyn_cast(NTD); if (CD && CD->getSuperclass()) { addTypeRef(CD->getSuperclass(), CD->getGenericSignature()); - } else if (PD && PD->getDeclaredType()->getSuperclass()) { - addTypeRef(PD->getDeclaredType()->getSuperclass(), + } else if (PD && PD->getDeclaredInterfaceType()->getSuperclass()) { + addTypeRef(PD->getDeclaredInterfaceType()->getSuperclass(), PD->getGenericSignature()); } else { B.addInt32(0); diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 1c5bd5eaf50c7..b8c647fbefc6d 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -52,7 +52,7 @@ using namespace irgen; enum class StructTypeInfoKind { LoadableStructTypeInfo, FixedStructTypeInfo, - ClangRecordTypeInfo, + LoadableClangRecordTypeInfo, NonFixedStructTypeInfo, ResilientStructTypeInfo }; @@ -292,17 +292,17 @@ namespace { }; /// A type implementation for loadable record types imported from Clang. - class ClangRecordTypeInfo final : - public StructTypeInfoBase { const clang::RecordDecl *ClangDecl; public: - ClangRecordTypeInfo(ArrayRef fields, + LoadableClangRecordTypeInfo(ArrayRef fields, unsigned explosionSize, llvm::Type *storageType, Size size, SpareBitVector &&spareBits, Alignment align, const clang::RecordDecl *clangDecl) - : StructTypeInfoBase(StructTypeInfoKind::ClangRecordTypeInfo, + : StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo, fields, explosionSize, storageType, size, std::move(spareBits), align, IsPOD, IsFixedSize), @@ -318,7 +318,7 @@ namespace { void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, Address addr, SILType T, bool isOutlined) const override { - ClangRecordTypeInfo::initialize(IGF, params, addr, isOutlined); + LoadableClangRecordTypeInfo::initialize(IGF, params, addr, isOutlined); } void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering, @@ -680,7 +680,7 @@ class ClangRecordLowering { const TypeInfo *createTypeInfo(llvm::StructType *llvmType) { llvmType->setBody(LLVMFields, /*packed*/ true); - return ClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex, + return LoadableClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex, llvmType, TotalStride, std::move(SpareBits), TotalAlignment, ClangDecl); @@ -865,8 +865,8 @@ class ClangRecordLowering { #define FOR_STRUCT_IMPL(IGF, type, op, ...) do { \ auto &structTI = IGF.getTypeInfo(type); \ switch (getStructTypeInfoKind(structTI)) { \ - case StructTypeInfoKind::ClangRecordTypeInfo: \ - return structTI.as().op(IGF, __VA_ARGS__); \ + case StructTypeInfoKind::LoadableClangRecordTypeInfo: \ + return structTI.as().op(IGF, __VA_ARGS__); \ case StructTypeInfoKind::LoadableStructTypeInfo: \ return structTI.as().op(IGF, __VA_ARGS__); \ case StructTypeInfoKind::FixedStructTypeInfo: \ diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index b49d8f1aad8a9..74d862df8d567 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -17,11 +17,13 @@ //===----------------------------------------------------------------------===// #include "Callee.h" +#include "ClassMetadataVisitor.h" #include "Explosion.h" #include "GenDecl.h" #include "GenClass.h" #include "GenHeap.h" #include "GenOpaque.h" +#include "GenPointerAuth.h" #include "GenProto.h" #include "IRGenFunction.h" #include "IRGenModule.h" @@ -171,6 +173,65 @@ void IRGenModule::emitMethodLookupFunction(ClassDecl *classDecl) { auto *description = getAddrOfTypeContextDescriptor(classDecl, RequireMetadata); + // Check for lookups of nonoverridden methods first. + class LookUpNonoverriddenMethods + : public ClassMetadataScanner { + + IRGenFunction &IGF; + llvm::Value *methodArg; + + public: + LookUpNonoverriddenMethods(IRGenFunction &IGF, + ClassDecl *classDecl, + llvm::Value *methodArg) + : ClassMetadataScanner(IGF.IGM, classDecl), IGF(IGF), + methodArg(methodArg) {} + + void noteNonoverriddenMethod(SILDeclRef method) { + // The method lookup function would be used only for `super.` calls + // from other modules, so we only need to look at public-visibility + // methods here. + if (!hasPublicVisibility(method.getLinkage(NotForDefinition))) { + return; + } + + auto methodDesc = IGM.getAddrOfMethodDescriptor(method, NotForDefinition); + + auto isMethod = IGF.Builder.CreateICmpEQ(methodArg, methodDesc); + + auto falseBB = IGF.createBasicBlock(""); + auto trueBB = IGF.createBasicBlock(""); + + IGF.Builder.CreateCondBr(isMethod, trueBB, falseBB); + + IGF.Builder.emitBlock(trueBB); + // Since this method is nonoverridden, we can produce a static result. + auto entry = VTable->getEntry(IGM.getSILModule(), method); + llvm::Value *impl = IGM.getAddrOfSILFunction(entry->getImplementation(), + NotForDefinition); + // Sign using the discriminator we would include in the method + // descriptor. + if (auto &schema = IGM.getOptions().PointerAuth.SwiftClassMethods) { + auto discriminator = + PointerAuthInfo::getOtherDiscriminator(IGM, schema, method); + + impl = emitPointerAuthSign(IGF, impl, + PointerAuthInfo(schema.getKey(), discriminator)); + } + impl = IGF.Builder.CreateBitCast(impl, IGM.Int8PtrTy); + IGF.Builder.CreateRet(impl); + + IGF.Builder.emitBlock(falseBB); + // Continue emission on the false branch. + } + + void noteResilientSuperclass() {} + void noteStartOfImmediateMembers(ClassDecl *clas) {} + }; + + LookUpNonoverriddenMethods(IGF, classDecl, method).layout(); + + // Use the runtime to look up vtable entries. auto *result = IGF.Builder.CreateCall(getLookUpClassMethodFn(), {metadata, method, description}); IGF.Builder.CreateRet(result); diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 7c8423d1f357b..1d99e48ca3f42 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1355,23 +1355,7 @@ TypeConverter::TypeConverter(IRGenModule &IGM) if (!doesPlatformUseLegacyLayouts(platformName, archName)) return; - // Find the first runtime library path that exists. - bool found = false; - for (auto &RuntimeLibraryPath - : IGM.Context.SearchPathOpts.RuntimeLibraryPaths) { - if (fs->exists(RuntimeLibraryPath)) { - defaultPath.append(RuntimeLibraryPath); - found = true; - break; - } - } - if (!found) { - auto joined = llvm::join(IGM.Context.SearchPathOpts.RuntimeLibraryPaths, - "', '"); - llvm::report_fatal_error("Unable to find a runtime library path at '" - + joined + "'"); - } - + defaultPath = IGM.Context.SearchPathOpts.RuntimeLibraryPaths[0]; llvm::sys::path::append(defaultPath, "layouts-"); defaultPath.append(archName); defaultPath.append(".yaml"); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 1211325f0877a..0d3d78dfec661 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -21,11 +21,12 @@ #include "swift/AST/IRGenRequests.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SILGenRequests.h" +#include "swift/AST/SILOptimizerRequests.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Dwarf.h" #include "swift/Basic/Platform.h" #include "swift/Basic/Statistic.h" -#include "swift/Basic/Timer.h" #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" @@ -223,7 +224,7 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts, addSwiftContractPass); } - if (RunSwiftSpecificLLVMOptzns) + if (!Opts.DisableSwiftSpecificLLVMOptzns) addCoroutinePassesToExtensionPoints(PMBuilder); if (Opts.Sanitizers & SanitizerKind::Address) { @@ -311,6 +312,13 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts, if (Opts.PrintInlineTree) ModulePasses.add(createInlineTreePrinterPass()); + // Make sure we do ARC contraction under optimization. We don't + // rely on any other LLVM ARC transformations, but we do need ARC + // contraction to add the objc_retainAutoreleasedReturnValue + // assembly markers and remove clang.arc.used. + if (Opts.shouldOptimize() && !DisableObjCARCContract) + ModulePasses.add(createObjCARCContractPass()); + // Do it. ModulePasses.run(*Module); @@ -358,23 +366,23 @@ class MD5Stream : public llvm::raw_ostream { /// Computes the MD5 hash of the llvm \p Module including the compiler version /// and options which influence the compilation. -static void getHashOfModule(MD5::MD5Result &Result, const IRGenOptions &Opts, - const llvm::Module *Module, - llvm::TargetMachine *TargetMachine, - version::Version const& effectiveLanguageVersion) { +static MD5::MD5Result getHashOfModule(const IRGenOptions &Opts, + const llvm::Module *Module) { // Calculate the hash of the whole llvm module. MD5Stream HashStream; llvm::WriteBitcodeToFile(*Module, HashStream); // Update the hash with the compiler version. We want to recompile if the // llvm pipeline of the compiler changed. - HashStream << version::getSwiftFullVersion(effectiveLanguageVersion); + HashStream << version::getSwiftFullVersion(); // Add all options which influence the llvm compilation but are not yet // reflected in the llvm module itself. Opts.writeLLVMCodeGenOptionsTo(HashStream); - HashStream.final(Result); + MD5::MD5Result result; + HashStream.final(result); + return result; } /// Returns false if the hash of the current module \p HashData matches the @@ -479,27 +487,24 @@ bool swift::performLLVM(const IRGenOptions &Opts, llvm::GlobalVariable *HashGlobal, llvm::Module *Module, llvm::TargetMachine *TargetMachine, - const version::Version &effectiveLanguageVersion, StringRef OutputFilename, UnifiedStatsReporter *Stats) { if (Opts.UseIncrementalLLVMCodeGen && HashGlobal) { // Check if we can skip the llvm part of the compilation if we have an // existing object file which was generated from the same llvm IR. - MD5::MD5Result Result; - getHashOfModule(Result, Opts, Module, TargetMachine, - effectiveLanguageVersion); + auto hash = getHashOfModule(Opts, Module); LLVM_DEBUG( if (DiagMutex) DiagMutex->lock(); SmallString<32> ResultStr; - MD5::stringifyResult(Result, ResultStr); + MD5::stringifyResult(hash, ResultStr); llvm::dbgs() << OutputFilename << ": MD5=" << ResultStr << '\n'; if (DiagMutex) DiagMutex->unlock(); ); - ArrayRef HashData(reinterpret_cast(&Result), - sizeof(Result)); + ArrayRef HashData(reinterpret_cast(&hash), + sizeof(hash)); if (Opts.OutputKind == IRGenOutputKind::ObjectFile && !Opts.PrintInlineTree && !needsRecompile(OutputFilename, HashData, HashGlobal, DiagMutex)) { @@ -534,69 +539,74 @@ bool swift::performLLVM(const IRGenOptions &Opts, performLLVMOptimizations(Opts, Module, TargetMachine); - legacy::PassManager EmitPasses; + if (Stats) { + if (DiagMutex) + DiagMutex->lock(); + countStatsPostIRGen(*Stats, *Module); + if (DiagMutex) + DiagMutex->unlock(); + } - // Make sure we do ARC contraction under optimization. We don't - // rely on any other LLVM ARC transformations, but we do need ARC - // contraction to add the objc_retainAutoreleasedReturnValue - // assembly markers and remove clang.arc.used. - if (Opts.shouldOptimize() && !DisableObjCARCContract) - EmitPasses.add(createObjCARCContractPass()); + if (!RawOS) + return false; + + return compileAndWriteLLVM(Module, TargetMachine, Opts, Stats, Diags, *RawOS, + DiagMutex); +} + +bool swift::compileAndWriteLLVM(llvm::Module *module, + llvm::TargetMachine *targetMachine, + const IRGenOptions &opts, + UnifiedStatsReporter *stats, + DiagnosticEngine &diags, + llvm::raw_pwrite_stream &out, + llvm::sys::Mutex *diagMutex) { + legacy::PassManager EmitPasses; // Set up the final emission passes. - switch (Opts.OutputKind) { + switch (opts.OutputKind) { case IRGenOutputKind::Module: break; case IRGenOutputKind::LLVMAssembly: - EmitPasses.add(createPrintModulePass(*RawOS)); + EmitPasses.add(createPrintModulePass(out)); break; case IRGenOutputKind::LLVMBitcode: { - if (Opts.LLVMLTOKind == IRGenLLVMLTOKind::Thin) { - EmitPasses.add(createWriteThinLTOBitcodePass(*RawOS)); + if (opts.LLVMLTOKind == IRGenLLVMLTOKind::Thin) { + EmitPasses.add(createWriteThinLTOBitcodePass(out)); } else { - EmitPasses.add(createBitcodeWriterPass(*RawOS)); + EmitPasses.add(createBitcodeWriterPass(out)); } break; } case IRGenOutputKind::NativeAssembly: case IRGenOutputKind::ObjectFile: { CodeGenFileType FileType; - FileType = (Opts.OutputKind == IRGenOutputKind::NativeAssembly - ? CGFT_AssemblyFile - : CGFT_ObjectFile); - + FileType = + (opts.OutputKind == IRGenOutputKind::NativeAssembly ? CGFT_AssemblyFile + : CGFT_ObjectFile); EmitPasses.add(createTargetTransformInfoWrapperPass( - TargetMachine->getTargetIRAnalysis())); + targetMachine->getTargetIRAnalysis())); - bool fail = TargetMachine->addPassesToEmitFile(EmitPasses, *RawOS, nullptr, - FileType, !Opts.Verify); + bool fail = targetMachine->addPassesToEmitFile(EmitPasses, out, nullptr, + FileType, !opts.Verify); if (fail) { - diagnoseSync(Diags, DiagMutex, - SourceLoc(), diag::error_codegen_init_fail); + diagnoseSync(diags, diagMutex, SourceLoc(), + diag::error_codegen_init_fail); return true; } break; } } - if (Stats) { - if (DiagMutex) - DiagMutex->lock(); - countStatsPostIRGen(*Stats, *Module); - if (DiagMutex) - DiagMutex->unlock(); - } - - EmitPasses.run(*Module); + EmitPasses.run(*module); - if (Stats && RawOS.hasValue()) { - if (DiagMutex) - DiagMutex->lock(); - Stats->getFrontendCounters().NumLLVMBytesOutput += RawOS->tell(); - if (DiagMutex) - DiagMutex->unlock(); + if (stats) { + if (diagMutex) + diagMutex->lock(); + stats->getFrontendCounters().NumLLVMBytesOutput += out.tell(); + if (diagMutex) + diagMutex->unlock(); } - return false; } @@ -905,15 +915,27 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, IRGenDescriptor desc) const { const auto &Opts = desc.Opts; const auto &PSPs = desc.PSPs; - - auto SILMod = std::unique_ptr(desc.SILMod); auto *M = desc.getParentModule(); - auto filesToEmit = desc.getFiles(); - auto *primaryFile = desc.Ctx.dyn_cast(); - auto &Ctx = M->getASTContext(); assert(!Ctx.hadError()); + // If we've been provided a SILModule, use it. Otherwise request the lowered + // SIL for the file or module. + auto SILMod = std::unique_ptr(desc.SILMod); + if (!SILMod) { + auto loweringDesc = + ASTLoweringDescriptor{desc.Ctx, desc.Conv, desc.SILOpts}; + SILMod = llvm::cantFail(Ctx.evaluator(LoweredSILRequest{loweringDesc})); + + // If there was an error, bail. + if (Ctx.hadError()) + return GeneratedModule::null(); + } + + auto filesToEmit = desc.getFiles(); + auto *primaryFile = + dyn_cast_or_null(desc.Ctx.dyn_cast()); + IRGenerator irgen(Opts, *SILMod); auto targetMachine = irgen.createTargetMachine(); @@ -933,7 +955,7 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, FrontendStatsTracer tracer(Ctx.Stats, "IRGen"); // Emit the module contents. - irgen.emitGlobalTopLevel(desc.LinkerDirectives); + irgen.emitGlobalTopLevel(desc.getLinkerDirectives()); for (auto *file : filesToEmit) { if (auto *nextSF = dyn_cast(file)) { @@ -988,7 +1010,7 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, if (Ctx.hadError()) return GeneratedModule::null(); // Free the memory occupied by the SILModule. - // Execute this task in parallel to the LLVM compilation. + // Execute this task in parallel to the embedding of bitcode. auto SILModuleRelease = [&SILMod]() { SILMod.reset(nullptr); }; auto Thread = std::thread(SILModuleRelease); // Wait for the thread to terminate. @@ -996,19 +1018,10 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, embedBitcode(IGM.getModule(), Opts); + // TODO: Turn the module hash into an actual output. if (auto **outModuleHash = desc.outModuleHash) { *outModuleHash = IGM.ModuleHash; - } else { - FrontendStatsTracer tracer(Ctx.Stats, "LLVM pipeline"); - - // Since no out module hash was set, we need to performLLVM. - if (performLLVM(Opts, IGM.Context.Diags, nullptr, IGM.ModuleHash, - IGM.getModule(), IGM.TargetMachine.get(), - IGM.Context.LangOpts.EffectiveLanguageVersion, - IGM.OutputFilename, IGM.Context.Stats)) - return GeneratedModule::null(); } - return std::move(IGM).intoGeneratedModule(); } @@ -1039,7 +1052,6 @@ struct LLVMCodeGenThreads { embedBitcode(IGM->getModule(), parent.irgen->Opts); performLLVM(parent.irgen->Opts, IGM->Context.Diags, diagMutex, IGM->ModuleHash, IGM->getModule(), IGM->TargetMachine.get(), - IGM->Context.LangOpts.EffectiveLanguageVersion, IGM->OutputFilename, IGM->Context.Stats); if (IGM->Context.Diags.hadAnyError()) return; @@ -1115,11 +1127,11 @@ struct LLVMCodeGenThreads { /// Generates LLVM IR, runs the LLVM passes and produces the output files. /// All this is done in multiple threads. -static void performParallelIRGeneration( - const IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, - StringRef ModuleName, - ArrayRef outputFilenames, - llvm::StringSet<> *linkerDirectives) { +static void performParallelIRGeneration(IRGenDescriptor desc) { + const auto &Opts = desc.Opts; + auto outputFilenames = desc.parallelOutputFilenames; + auto SILMod = std::unique_ptr(desc.SILMod); + auto *M = desc.getParentModule(); IRGenerator irgen(Opts, *SILMod); @@ -1160,7 +1172,7 @@ static void performParallelIRGeneration( // Create the IR emitter. IRGenModule *IGM = new IRGenModule(irgen, std::move(targetMachine), nextSF, - ModuleName, *OutputIter++, nextSF->getFilename(), + desc.ModuleName, *OutputIter++, nextSF->getFilename(), nextSF->getPrivateDiscriminator().str()); IGMcreated = true; @@ -1180,7 +1192,7 @@ static void performParallelIRGeneration( } // Emit the module contents. - irgen.emitGlobalTopLevel(linkerDirectives); + irgen.emitGlobalTopLevel(desc.getLinkerDirectives()); for (auto *File : M->getFiles()) { if (auto *SF = dyn_cast(File)) { @@ -1308,37 +1320,42 @@ static void performParallelIRGeneration( } GeneratedModule swift::performIRGeneration( - const IRGenOptions &Opts, swift::ModuleDecl *M, - std::unique_ptr SILMod, StringRef ModuleName, - const PrimarySpecificPaths &PSPs, + swift::ModuleDecl *M, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, std::unique_ptr SILMod, + StringRef ModuleName, const PrimarySpecificPaths &PSPs, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash, llvm::StringSet<> *LinkerDirectives) { + llvm::GlobalVariable **outModuleHash) { + // Get a pointer to the SILModule to avoid a potential use-after-move. + const auto *SILModPtr = SILMod.get(); + const auto &SILOpts = SILModPtr->getOptions(); + auto desc = IRGenDescriptor::forWholeModule( + M, Opts, TBDOpts, SILOpts, SILModPtr->Types, std::move(SILMod), + ModuleName, PSPs, parallelOutputFilenames, outModuleHash); + if (Opts.shouldPerformIRGenerationInParallel() && !parallelOutputFilenames.empty()) { - ::performParallelIRGeneration(Opts, M, std::move(SILMod), ModuleName, - parallelOutputFilenames, LinkerDirectives); + ::performParallelIRGeneration(desc); // TODO: Parallel LLVM compilation cannot be used if a (single) module is // needed as return value. return GeneratedModule::null(); } - - auto desc = IRGenDescriptor::forWholeModule( - Opts, M, std::move(SILMod), ModuleName, PSPs, parallelOutputFilenames, - outModuleHash, LinkerDirectives); return llvm::cantFail(M->getASTContext().evaluator(IRGenRequest{desc})); } GeneratedModule swift:: -performIRGeneration(const IRGenOptions &Opts, SourceFile &SF, +performIRGeneration(FileUnit *file, const IRGenOptions &Opts, + const TBDGenOptions &TBDOpts, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, StringRef PrivateDiscriminator, - llvm::GlobalVariable **outModuleHash, - llvm::StringSet<> *LinkerDirectives) { - auto desc = IRGenDescriptor::forFile(Opts, SF, std::move(SILMod), ModuleName, - PSPs, PrivateDiscriminator, - outModuleHash, LinkerDirectives); - return llvm::cantFail(SF.getASTContext().evaluator(IRGenRequest{desc})); + llvm::GlobalVariable **outModuleHash) { + // Get a pointer to the SILModule to avoid a potential use-after-move. + const auto *SILModPtr = SILMod.get(); + const auto &SILOpts = SILModPtr->getOptions(); + auto desc = IRGenDescriptor::forFile( + file, Opts, TBDOpts, SILOpts, SILModPtr->Types, std::move(SILMod), + ModuleName, PSPs, PrivateDiscriminator, outModuleHash); + return llvm::cantFail(file->getASTContext().evaluator(IRGenRequest{desc})); } void @@ -1391,7 +1408,6 @@ swift::createSwiftModuleObjectFile(SILModule &SILMod, StringRef Buffer, ASTSym->setAlignment(llvm::MaybeAlign(serialization::SWIFTMODULE_ALIGNMENT)); ::performLLVM(Opts, Ctx.Diags, nullptr, nullptr, IGM.getModule(), IGM.TargetMachine.get(), - Ctx.LangOpts.EffectiveLanguageVersion, OutputPath, Ctx.Stats); } @@ -1409,8 +1425,38 @@ bool swift::performLLVM(const IRGenOptions &Opts, ASTContext &Ctx, embedBitcode(Module, Opts); if (::performLLVM(Opts, Ctx.Diags, nullptr, nullptr, Module, TargetMachine.get(), - Ctx.LangOpts.EffectiveLanguageVersion, OutputFilename, Ctx.Stats)) return true; return false; } + +GeneratedModule OptimizedIRRequest::evaluate(Evaluator &evaluator, + IRGenDescriptor desc) const { + auto *parentMod = desc.getParentModule(); + auto &ctx = parentMod->getASTContext(); + + // Resolve imports for all the source files. + for (auto *file : parentMod->getFiles()) { + if (auto *SF = dyn_cast(file)) + performImportResolution(*SF); + } + + bindExtensions(*parentMod); + + // Type-check the files that need emitting. + for (auto *file : desc.getFiles()) { + if (auto *SF = dyn_cast(file)) + performTypeChecking(*SF); + } + + if (ctx.hadError()) + return GeneratedModule::null(); + + auto irMod = llvm::cantFail(evaluator(IRGenRequest{desc})); + if (!irMod) + return irMod; + + performLLVMOptimizations(desc.Opts, irMod.getModule(), + irMod.getTargetMachine()); + return irMod; +} diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index b15fb6a69a6a1..e1cd39d903dde 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -948,7 +948,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { } else { // Discriminated union case without argument. Fallback to Int // as the element type; there is no storage here. - Type IntTy = IGM.Context.getIntDecl()->getDeclaredType(); + Type IntTy = IGM.Context.getIntDecl()->getDeclaredInterfaceType(); ElemDbgTy = DebugTypeInfo(IntTy, DbgTy.getStorageType(), Size(0), Alignment(1), true, false); } @@ -1586,6 +1586,13 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { /// Determine if there exists a name mangling for the given type. static bool canMangle(TypeBase *Ty) { + // TODO: C++ types are not yet supported (SR-13223). + if (Ty->getStructOrBoundGenericStruct() && + Ty->getStructOrBoundGenericStruct()->getClangDecl() && + isa( + Ty->getStructOrBoundGenericStruct()->getClangDecl())) + return false; + switch (Ty->getKind()) { case TypeKind::GenericFunction: // Not yet supported. case TypeKind::SILBlockStorage: // Not supported at all. @@ -2378,6 +2385,17 @@ void IRGenDebugInfoImpl::emitGlobalVariableDeclaration( if (Opts.DebugInfoLevel <= IRGenDebugInfoLevel::LineTables) return; + // TODO: fix demangling for C++ types (SR-13223). + if (swift::TypeBase *ty = DbgTy.getType()) { + if (MetatypeType *metaTy = dyn_cast(ty)) + ty = metaTy->getInstanceType().getPointer(); + if (ty->getStructOrBoundGenericStruct() && + ty->getStructOrBoundGenericStruct()->getClangDecl() && + isa( + ty->getStructOrBoundGenericStruct()->getClangDecl())) + return; + } + llvm::DIType *DITy = getOrCreateType(DbgTy); VarDecl *VD = nullptr; if (Loc) diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index e83e186519ed2..720a85ecc9887 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -143,6 +143,8 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, }); } + + std::string IRGenMangler::mangleProtocolConformanceDescriptor( const RootProtocolConformance *conformance) { beginMangling(); @@ -157,6 +159,21 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( return finalize(); } +std::string IRGenMangler::mangleProtocolConformanceInstantiationCache( + const RootProtocolConformance *conformance) { + beginMangling(); + if (isa(conformance)) { + appendProtocolConformance(conformance); + appendOperator("Mc"); + } else { + auto protocol = cast(conformance)->getProtocol(); + appendProtocolName(protocol); + appendOperator("MS"); + } + appendOperator("MK"); + return finalize(); +} + SymbolicMangling IRGenMangler::mangleProtocolConformanceForReflection(IRGenModule &IGM, Type ty, ProtocolConformanceRef conformance) { diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index 98e29b27bcb5f..966465a37a184 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -118,6 +118,14 @@ class IRGenMangler : public Mangle::ASTMangler { return mangleTypeSymbol(type, "N"); } + std::string mangleNoncanonicalTypeMetadata(Type type) { + return mangleTypeSymbol(type, "MN"); + } + + std::string mangleNoncanonicalSpecializedGenericTypeMetadataCache(Type type) { + return mangleTypeSymbol(type, "MJ"); + } + std::string mangleTypeMetadataPattern(const NominalTypeDecl *decl) { return mangleNominalTypeSymbol(decl, "MP"); } @@ -315,8 +323,10 @@ class IRGenMangler : public Mangle::ASTMangler { } std::string mangleProtocolConformanceDescriptor( - const RootProtocolConformance *conformance); - + const RootProtocolConformance *conformance); + std::string mangleProtocolConformanceInstantiationCache( + const RootProtocolConformance *conformance); + std::string manglePropertyDescriptor(const AbstractStorageDecl *storage) { beginMangling(); appendEntity(storage); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 7abb51b6e1e40..b2b0cb3a7d2d9 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -271,6 +271,7 @@ IRGenModule::IRGenModule(IRGenerator &irgen, MetadataKindTy // MetadataKind Kind; }); TypeMetadataPtrTy = TypeMetadataStructTy->getPointerTo(DefaultAS); + TypeMetadataPtrPtrTy = TypeMetadataPtrTy->getPointerTo(DefaultAS); TypeMetadataResponseTy = createStructType(*this, "swift.metadata_response", { TypeMetadataPtrTy, @@ -678,6 +679,16 @@ namespace RuntimeConstants { } return RuntimeAvailability::AlwaysAvailable; } + + RuntimeAvailability + GetCanonicalSpecializedMetadataAvailability(ASTContext &context) { + auto featureAvailability = + context.getIntermodulePrespecializedGenericMetadataAvailability(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the @@ -955,6 +966,7 @@ GeneratedModule IRGenModule::intoGeneratedModule() && { return GeneratedModule{ std::move(LLVMContext), std::unique_ptr{ClangCodeGen->ReleaseModule()}, + std::move(TargetMachine) }; } diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index d8634e056470d..772301bd07e97 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -106,6 +106,7 @@ namespace swift { class SILModule; class SILProperty; class SILType; + class SILVTable; class SILWitnessTable; class SourceLoc; class SourceFile; @@ -375,7 +376,7 @@ class IRGenerator { /// Emit functions, variables and tables which are needed anyway, e.g. because /// they are externally visible. - void emitGlobalTopLevel(llvm::StringSet<> *LinkerDirectives); + void emitGlobalTopLevel(const std::vector &LinkerDirectives); /// Emit references to each of the protocol descriptors defined in this /// IR module. @@ -399,6 +400,9 @@ class IRGenerator { // Emit the code to replace dynamicReplacement(for:) functions. void emitDynamicReplacements(); + // Emit info that describes the entry point to the module, if it has one. + void emitEntryPointInfo(); + /// Checks if metadata for this type can be emitted lazily. This is true for /// non-public types as well as imported types, except for classes and /// protocols which are always emitted eagerly. @@ -543,6 +547,11 @@ enum class MangledTypeRefRole { DefaultAssociatedTypeWitness, }; +enum class TypeMetadataCanonicality : bool { + Noncanonical, + Canonical, +}; + /// IRGenModule - Primary class for emitting IR for global declarations. /// class IRGenModule { @@ -635,6 +644,7 @@ class IRGenModule { llvm::FunctionType *DeallocatingDtorTy; /// void (%swift.refcounted*) llvm::StructType *TypeMetadataStructTy; /// %swift.type = type { ... } llvm::PointerType *TypeMetadataPtrTy;/// %swift.type* + llvm::PointerType *TypeMetadataPtrPtrTy; /// %swift.type** union { llvm::StructType *TypeMetadataResponseTy; /// { %swift.type*, iSize } llvm::StructType *TypeMetadataDependencyTy; /// { %swift.type*, iSize } @@ -1367,6 +1377,8 @@ private: \ llvm::Constant *definition); llvm::Constant *getAddrOfMethodDescriptor(SILDeclRef declRef, ForDefinition_t forDefinition); + void emitNonoverriddenMethodDescriptor(const SILVTable *VTable, + SILDeclRef declRef); Address getAddrOfEnumCase(EnumElementDecl *Case, ForDefinition_t forDefinition); @@ -1391,9 +1403,14 @@ private: \ TypeEntityReference getTypeEntityReference(GenericTypeDecl *D); - llvm::Constant *getAddrOfTypeMetadata(CanType concreteType); - ConstantReference getAddrOfTypeMetadata(CanType concreteType, - SymbolReferenceKind kind); + llvm::Constant * + getAddrOfTypeMetadata(CanType concreteType, + TypeMetadataCanonicality canonicality = + TypeMetadataCanonicality::Canonical); + ConstantReference + getAddrOfTypeMetadata(CanType concreteType, SymbolReferenceKind kind, + TypeMetadataCanonicality canonicality = + TypeMetadataCanonicality::Canonical); llvm::Constant *getAddrOfTypeMetadataPattern(NominalTypeDecl *D); llvm::Constant *getAddrOfTypeMetadataPattern(NominalTypeDecl *D, ConstantInit init, @@ -1419,6 +1436,7 @@ private: \ llvm::Constant *getAddrOfTypeMetadataLazyCacheVariable(CanType type); llvm::Constant *getAddrOfTypeMetadataDemanglingCacheVariable(CanType type, ConstantInit definition); + llvm::Constant *getAddrOfNoncanonicalSpecializedGenericTypeMetadataCacheVariable(CanType type); llvm::Constant *getAddrOfClassMetadataBounds(ClassDecl *D, ForDefinition_t forDefinition); diff --git a/lib/IRGen/IRGenRequests.cpp b/lib/IRGen/IRGenRequests.cpp index 0bb8963b44984..7cff6f7782837 100644 --- a/lib/IRGen/IRGenRequests.cpp +++ b/lib/IRGen/IRGenRequests.cpp @@ -17,6 +17,7 @@ #include "swift/AST/SourceFile.h" #include "swift/SIL/SILModule.h" #include "swift/Subsystems.h" +#include "swift/TBDGen/TBDGen.h" #include "llvm/IR/Module.h" #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" @@ -38,13 +39,12 @@ llvm::orc::ThreadSafeModule GeneratedModule::intoThreadSafeContext() && { void swift::simple_display(llvm::raw_ostream &out, const IRGenDescriptor &desc) { auto *MD = desc.Ctx.dyn_cast(); - auto *SF = desc.Ctx.dyn_cast(); if (MD) { out << "IR Generation for module " << MD->getName(); } else { - assert(SF); + auto *file = desc.Ctx.get(); out << "IR Generation for file "; - out << '\"' << SF->getFilename() << '\"'; + simple_display(out, file); } } @@ -57,24 +57,36 @@ TinyPtrVector IRGenDescriptor::getFiles() const { if (auto *mod = Ctx.dyn_cast()) return TinyPtrVector(mod->getFiles()); - // For a primary source file, we emit IR for both it and potentially its + // For a primary file, we emit IR for both it and potentially its // SynthesizedFileUnit. - auto *SF = Ctx.get(); + auto *primary = Ctx.get(); TinyPtrVector files; - files.push_back(SF); - - if (auto *synthesizedFile = SF->getSynthesizedFile()) - files.push_back(synthesizedFile); + files.push_back(primary); + if (auto *SF = dyn_cast(primary)) { + if (auto *synthesizedFile = SF->getSynthesizedFile()) + files.push_back(synthesizedFile); + } return files; } ModuleDecl *IRGenDescriptor::getParentModule() const { - if (auto *SF = Ctx.dyn_cast()) - return SF->getParentModule(); + if (auto *file = Ctx.dyn_cast()) + return file->getParentModule(); return Ctx.get(); } +std::vector IRGenDescriptor::getLinkerDirectives() const { + auto opts = TBDOpts; + opts.LinkerDirectivesOnly = true; + if (auto *file = Ctx.dyn_cast()) { + return getPublicSymbols(TBDGenDescriptor::forFile(file, opts)); + } else { + auto *M = Ctx.get(); + return getPublicSymbols(TBDGenDescriptor::forModule(M, opts)); + } +} + evaluator::DependencySource IRGenRequest::readDependencySource( const evaluator::DependencyRecorder &e) const { auto &desc = std::get<0>(getStorage()); @@ -84,8 +96,8 @@ evaluator::DependencySource IRGenRequest::readDependencySource( return {nullptr, e.getActiveSourceScope()}; } - auto *SF = desc.Ctx.get(); - return {SF, evaluator::DependencyScope::Cascading}; + auto *primary = desc.Ctx.get(); + return {dyn_cast(primary), evaluator::DependencyScope::Cascading}; } // Define request evaluation functions for each of the IRGen requests. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 8f395d6772610..cd6a850512f27 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -37,6 +37,7 @@ #include "swift/SIL/SILType.h" #include "swift/SIL/SILVisitor.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "llvm/ADT/MapVector.h" @@ -817,6 +818,17 @@ class IRGenSILFunction : SILType SILTy, const SILDebugScope *DS, VarDecl *VarDecl, SILDebugVariable VarInfo, IndirectionKind Indirection = DirectValue) { + // TODO: fix demangling for C++ types (SR-13223). + if (swift::TypeBase *ty = SILTy.getASTType().getPointer()) { + if (MetatypeType *metaTy = dyn_cast(ty)) + ty = metaTy->getRootClass().getPointer(); + if (ty->getStructOrBoundGenericStruct() && + ty->getStructOrBoundGenericStruct()->getClangDecl() && + isa( + ty->getStructOrBoundGenericStruct()->getClangDecl())) + return; + } + assert(IGM.DebugInfo && "debug info not enabled"); if (VarInfo.ArgNo) { PrologueLocation AutoRestore(IGM.DebugInfo.get(), Builder); diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 5dec354c1d01b..f43af5ccc07a3 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -182,6 +182,12 @@ std::string LinkEntity::mangleAsString() const { } llvm_unreachable("invalid metadata address"); + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + return mangler.mangleNoncanonicalTypeMetadata(getType()); + + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: + return mangler.mangleNoncanonicalSpecializedGenericTypeMetadataCache(getType()); + case Kind::TypeMetadataPattern: return mangler.mangleTypeMetadataPattern( cast(getDecl())); @@ -511,6 +517,12 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { llvm_unreachable("bad kind"); } + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: + // Prespecialization of the same non-canonical generic metadata may be + // requested multiple times, so it needs to be uniqued. + return SILLinkage::Shared; + // ...but we don't actually expose individual value witnesses (right now). case Kind::ValueWitness: { auto *nominal = getType().getAnyNominal(); @@ -753,6 +765,8 @@ bool LinkEntity::isContextDescriptor() const { case Kind::OpaqueTypeDescriptorAccessorVar: case Kind::DifferentiabilityWitness: case Kind::CanonicalSpecializedGenericSwiftMetaclassStub: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: return false; } llvm_unreachable("invalid descriptor"); @@ -785,6 +799,7 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const { case Kind::CanonicalSpecializedGenericSwiftMetaclassStub: return IGM.ObjCClassStructTy; case Kind::TypeMetadataLazyCacheVariable: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: return IGM.TypeMetadataPtrTy; case Kind::TypeMetadataDemanglingCacheVariable: return llvm::StructType::get(IGM.Int32Ty, IGM.Int32Ty); @@ -793,6 +808,7 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const { return llvm::StructType::get(IGM.getLLVMContext(), {IGM.TypeMetadataPtrTy, IGM.Int8PtrTy}); case Kind::TypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: switch (getMetadataAddress()) { case TypeMetadataAddress::FullMetadata: if (getType().getClassOrBoundGenericClass()) @@ -907,6 +923,8 @@ Alignment LinkEntity::getAlignment(IRGenModule &IGM) const { case Kind::OpaqueTypeDescriptorAccessorVar: case Kind::ObjCResilientClassStub: case Kind::DifferentiabilityWitness: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: return IGM.getPointerAlignment(); case Kind::TypeMetadataDemanglingCacheVariable: return Alignment(8); @@ -944,8 +962,7 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { return cast(getDecl())->isWeakImported(module); case Kind::TypeMetadata: - case Kind::TypeMetadataAccessFunction: - case Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: { + case Kind::TypeMetadataAccessFunction: { if (auto *nominalDecl = getType()->getAnyNominal()) return nominalDecl->isWeakImported(module); return false; @@ -989,6 +1006,11 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { return getProtocolConformance()->getRootConformance() ->isWeakImported(module); + case Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: + return false; + // TODO: Revisit some of the below, for weak conformances. case Kind::ObjCMetadataUpdateFunction: case Kind::ObjCResilientClassStub: @@ -1104,6 +1126,8 @@ DeclContext *LinkEntity::getDeclContextForEmission() const { case Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: case Kind::TypeMetadataLazyCacheVariable: case Kind::TypeMetadataDemanglingCacheVariable: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: assert(isAlwaysSharedLinkage() && "kind should always be shared linkage"); return nullptr; @@ -1129,6 +1153,8 @@ bool LinkEntity::isAlwaysSharedLinkage() const { case Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: case Kind::TypeMetadataLazyCacheVariable: case Kind::TypeMetadataDemanglingCacheVariable: + case Kind::NoncanonicalSpecializedGenericTypeMetadata: + case Kind::NoncanonicalSpecializedGenericTypeMetadataCacheVariable: return true; default: diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index f8cbaf4b57948..e93d370240ea4 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -337,6 +337,14 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl) } super::addReifiedVTableEntry(fn); } + + void noteNonoverriddenMethod(SILDeclRef fn) { + if (fn.getDecl()->getDeclContext() == Target) { + auto impl = VTable->getEntry(IGM.getSILModule(), fn); + Layout.MethodInfos.try_emplace(fn, + IGM.getAddrOfSILFunction(impl->getImplementation(), NotForDefinition)); + } + } void noteStartOfFieldOffsets(ClassDecl *forClass) { if (forClass == Target) @@ -398,8 +406,14 @@ Size ClassMetadataLayout::getInstanceAlignMaskOffset() const { ClassMetadataLayout::MethodInfo ClassMetadataLayout::getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const{ auto &stored = getStoredMethodInfo(method); - auto offset = emitOffset(IGF, stored.TheOffset); - return MethodInfo(offset); + switch (stored.TheKind) { + case MethodInfo::Kind::Offset: { + auto offset = emitOffset(IGF, stored.TheOffset); + return MethodInfo(offset); + } + case MethodInfo::Kind::DirectImpl: + return MethodInfo(stored.TheImpl); + } } Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF, diff --git a/lib/IRGen/MetadataLayout.h b/lib/IRGen/MetadataLayout.h index 8e5d92cee83ce..312b8c55876f5 100644 --- a/lib/IRGen/MetadataLayout.h +++ b/lib/IRGen/MetadataLayout.h @@ -168,11 +168,34 @@ class NominalMetadataLayout : public MetadataLayout { class ClassMetadataLayout : public NominalMetadataLayout { public: class MethodInfo { - Offset TheOffset; + public: + enum class Kind { + Offset, + DirectImpl, + }; + + private: + Kind TheKind; + union { + Offset TheOffset; + llvm::Function *TheImpl; + }; public: MethodInfo(Offset offset) - : TheOffset(offset) {} - Offset getOffset() const { return TheOffset; } + : TheKind(Kind::Offset), TheOffset(offset) {} + MethodInfo(llvm::Function *impl) + : TheKind(Kind::DirectImpl), TheImpl(impl) {} + + Kind getKind() const { return TheKind; } + + Offset getOffsett() const { + assert(getKind() == Kind::Offset); + return TheOffset; + } + llvm::Function *getDirectImpl() const { + assert(getKind() == Kind::DirectImpl); + return TheImpl; + } }; private: @@ -187,8 +210,16 @@ class ClassMetadataLayout : public NominalMetadataLayout { StoredOffset InstanceAlignMask; struct StoredMethodInfo { - StoredOffset TheOffset; - StoredMethodInfo(StoredOffset offset) : TheOffset(offset) {} + MethodInfo::Kind TheKind; + union { + StoredOffset TheOffset; + llvm::Function *TheImpl; + }; + StoredMethodInfo(StoredOffset offset) : TheKind(MethodInfo::Kind::Offset), + TheOffset(offset) {} + StoredMethodInfo(llvm::Function *impl) + : TheKind(MethodInfo::Kind::DirectImpl), + TheImpl(impl) {} }; llvm::DenseMap MethodInfos; diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 57e4938218f45..8f33b91a85fef 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -546,7 +546,7 @@ irgen::tryEmitConstantTypeMetadataRef(IRGenModule &IGM, CanType type, SymbolReferenceKind refKind) { if (IGM.isStandardLibrary()) return ConstantReference(); - if (isCompleteTypeMetadataStaticallyAddressable(IGM, type)) + if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) return ConstantReference(); return IGM.getAddrOfTypeMetadata(type, refKind); } @@ -583,9 +583,10 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF, static MetadataResponse emitNominalPrespecializedGenericMetadataRef( IRGenFunction &IGF, NominalTypeDecl *theDecl, CanType theType, - DynamicMetadataRequest request) { - assert(isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGF.IGM, *theDecl, theType)); + DynamicMetadataRequest request, + SpecializedMetadataCanonicality canonicality) { + assert(isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGF.IGM, *theDecl, theType, canonicality)); // We are applying generic parameters to a generic type. assert(theType->getAnyNominal() == theDecl); @@ -593,8 +594,27 @@ static MetadataResponse emitNominalPrespecializedGenericMetadataRef( if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request)) return cache; - auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType); - return MetadataResponse::forComplete(metadata); + switch (canonicality) { + case CanonicalSpecializedMetadata: { + auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType); + return MetadataResponse::forComplete(metadata); + } + case NoncanonicalSpecializedMetadata: { + auto cacheVariable = + IGF.IGM.getAddrOfNoncanonicalSpecializedGenericTypeMetadataCacheVariable(theType); + auto call = IGF.Builder.CreateCall( + IGF.IGM.getGetCanonicalSpecializedMetadataFn(), + {request.get(IGF), + IGF.IGM.getAddrOfTypeMetadata(theType, + TypeMetadataCanonicality::Noncanonical), + cacheVariable}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); + return MetadataResponse::handle(IGF, request, call); + } + } } static llvm::Value * @@ -666,13 +686,17 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, MetadataResponse response; - if (isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGF.IGM, *theDecl, theType)) { - response = emitNominalPrespecializedGenericMetadataRef(IGF, theDecl, - theType, request); + if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGF.IGM, *theDecl, theType, CanonicalSpecializedMetadata)) { + response = emitNominalPrespecializedGenericMetadataRef( + IGF, theDecl, theType, request, CanonicalSpecializedMetadata); + } else if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGF.IGM, *theDecl, theType, NoncanonicalSpecializedMetadata)) { + response = emitNominalPrespecializedGenericMetadataRef( + IGF, theDecl, theType, request, NoncanonicalSpecializedMetadata); } else if (auto theClass = dyn_cast(theDecl)) { - if (isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGF.IGM, *theClass, theType, + if (isSpecializedNominalTypeMetadataStaticallyAddressable( + IGF.IGM, *theClass, theType, CanonicalSpecializedMetadata, ForUseOnlyFromAccessor)) { llvm::Function *accessor = IGF.IGM @@ -698,9 +722,10 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, return response; } -bool irgen::isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( +bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable( IRGenModule &IGM, NominalTypeDecl &nominal, CanType type, - CanonicalSpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor) { + SpecializedMetadataCanonicality canonicality, + SpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor) { assert(nominal.isGenericContext()); if (!IGM.shouldPrespecializeGenericMetadata()) { @@ -711,30 +736,40 @@ bool irgen::isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( return false; } - if (IGM.getSILModule().isWholeModule()) { - // Canonical prespecializations can only be emitted within the module where - // the generic type is itself defined, since it is the module where the - // metadata accessor is defined. - if (IGM.getSwiftModule() != nominal.getModuleContext()) { - return false; - } - } else { - // If whole module optimization is not enabled, we can only construct a - // canonical prespecialization if the usage is in the same *file* as that - // containing the type's decl! The reason is that the generic metadata - // accessor is defined in the IRGenModule corresponding to the source file - // containing the type's decl. - SourceFile *nominalFile = nominal.getDeclContext()->getParentSourceFile(); - if (auto *moduleFile = IGM.IRGen.getSourceFile(&IGM)) { - if (nominalFile != moduleFile) { + switch (canonicality) { + case CanonicalSpecializedMetadata: + if (IGM.getSILModule().isWholeModule()) { + // Canonical prespecializations can only be emitted within the module + // where the generic type is itself defined, since it is the module where + // the metadata accessor is defined. + if (IGM.getSwiftModule() != nominal.getModuleContext()) { return false; } + } else { + // If whole module optimization is not enabled, we can only construct a + // canonical prespecialization if the usage is in the same *file* as that + // containing the type's decl! The reason is that the generic metadata + // accessor is defined in the IRGenModule corresponding to the source file + // containing the type's decl. + SourceFile *nominalFile = nominal.getDeclContext()->getParentSourceFile(); + if (auto *moduleFile = IGM.IRGen.getSourceFile(&IGM)) { + if (nominalFile != moduleFile) { + return false; + } + } } - } - - if (nominal.isResilient(IGM.getSwiftModule(), - ResilienceExpansion::Maximal)) { - return false; + break; + case NoncanonicalSpecializedMetadata: + // Non-canonical metadata prespecializations for a type cannot be formed + // within the module that defines that type. + if (IGM.getSwiftModule() == nominal.getModuleContext()) { + return false; + } + if (nominal.isResilient(IGM.getSwiftModule(), + ResilienceExpansion::Maximal)) { + return false; + } + break; } if (auto *theClass = dyn_cast(&nominal)) { @@ -750,8 +785,8 @@ bool irgen::isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( if (auto *theSuperclass = theClass->getSuperclassDecl()) { auto superclassType = type->getSuperclass(/*useArchetypes=*/false)->getCanonicalType(); - if (!isInitializableTypeMetadataStaticallyAddressable(IGM, - superclassType) && + if (!isCanonicalInitializableTypeMetadataStaticallyAddressable( + IGM, superclassType) && !tryEmitConstantHeapMetadataRef( IGM, superclassType, /*allowDynamicUninitialized=*/false)) { @@ -767,52 +802,107 @@ bool irgen::isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( auto substitutions = type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal); - auto allWitnessTablesAreReferenceable = llvm::all_of(environment->getGenericParams(), [&](auto parameter) { - auto signature = environment->getGenericSignature(); - const auto protocols = signature->getRequiredProtocols(parameter); - auto argument = ((Type *)parameter)->subst(substitutions); - auto canonicalType = argument->getCanonicalType(); - auto witnessTablesAreReferenceable = [&]() { - return llvm::all_of(protocols, [&](ProtocolDecl *protocol) { - auto conformance = - signature->lookupConformance(canonicalType, protocol); - if (!conformance.isConcrete()) { - return false; - } - auto rootConformance = conformance.getConcrete()->getRootConformance(); - return !IGM.isDependentConformance(rootConformance) && - !IGM.isResilientConformance(rootConformance); + auto allArgumentsAreStaticallyAddressable = + llvm::all_of(environment->getGenericParams(), [&](auto parameter) { + auto signature = environment->getGenericSignature(); + const auto protocols = signature->getRequiredProtocols(parameter); + auto argument = ((Type *)parameter)->subst(substitutions); + auto canonicalType = argument->getCanonicalType(); + auto witnessTablesAreReferenceable = [&]() { + return llvm::all_of(protocols, [&](ProtocolDecl *protocol) { + auto conformance = + signature->lookupConformance(canonicalType, protocol); + if (!conformance.isConcrete()) { + return false; + } + auto rootConformance = + conformance.getConcrete()->getRootConformance(); + return !IGM.isDependentConformance(rootConformance) && + !IGM.isResilientConformance(rootConformance); + }); + }; + // TODO: Once witness tables are statically specialized, check whether + // the + // ConformanceInfo returns nullptr from tryGetConstantTable. + auto isGenericWithoutPrespecializedConformance = [&]() { + auto genericArgument = argument->getAnyGeneric(); + return genericArgument && genericArgument->isGenericContext() && + (protocols.size() > 0); + }; + auto metadataAccessIsTrivial = [&]() { + if (onlyFromAccessor) { + // If an accessor is being used, then the accessor will be able to + // initialize the arguments, i.e. register classes with the ObjC + // runtime. + return irgen:: + isCanonicalInitializableTypeMetadataStaticallyAddressable( + IGM, canonicalType); + } else { + return irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable( + IGM, canonicalType); + } + }; + return !isGenericWithoutPrespecializedConformance() && + metadataAccessIsTrivial() && witnessTablesAreReferenceable(); }); - }; - // TODO: Once witness tables are statically specialized, check whether the - // ConformanceInfo returns nullptr from tryGetConstantTable. - auto isGenericWithoutPrespecializedConformance = [&]() { - auto genericArgument = argument->getAnyGeneric(); - return genericArgument && genericArgument->isGenericContext() && - (protocols.size() > 0); - }; - auto metadataAccessIsTrivial = [&]() { - if (onlyFromAccessor) { - // If an accessor is being used, then the accessor will be able to - // initialize the arguments, i.e. register classes with the ObjC - // runtime. - return irgen::isInitializableTypeMetadataStaticallyAddressable( - IGM, argument->getCanonicalType()); - } else { - return irgen::isCompleteTypeMetadataStaticallyAddressable( - IGM, argument->getCanonicalType()); - } - }; - return !isGenericWithoutPrespecializedConformance() && - metadataAccessIsTrivial() && witnessTablesAreReferenceable(); - }); - return allWitnessTablesAreReferenceable - && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); -} + auto anyArgumentIsFromCurrentModule = + llvm::any_of(environment->getGenericParams(), [&](auto parameter) { + auto signature = environment->getGenericSignature(); + const auto protocols = signature->getRequiredProtocols(parameter); + auto argument = ((Type *)parameter)->subst(substitutions); + auto canonicalType = argument->getCanonicalType(); + + auto argumentIsFromCurrentModule = [&]() { + if (auto *argumentNominal = argument->getAnyNominal()) { + return IGM.getSwiftModule() == argumentNominal->getModuleContext(); + } + return false; + }; + auto anyConformanceIsFromCurrentModule = [&]() { + return llvm::any_of(protocols, [&](ProtocolDecl *protocol) { + auto conformance = + signature->lookupConformance(canonicalType, protocol); + if (!conformance.isConcrete()) { + return false; + } + auto rootConformance = + conformance.getConcrete()->getRootConformance(); + return IGM.getSwiftModule() == + rootConformance->getDeclContext()->getParentModule(); + }); + }; -bool irgen:: - isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IRGenModule &IGM, NominalTypeDecl &nominal, CanType type) { + return argumentIsFromCurrentModule() || + anyConformanceIsFromCurrentModule(); + }); + return allArgumentsAreStaticallyAddressable && + // A type's metadata cannot be prespecialized non-canonically if it + // could be specialized canonically. The reasons for that: + // (1) Canonically prespecialized metadata is not registered with the + // runtime; at runtime, whether canonically prespecialized + // metadata exists can only be determined by calling the metadata + // accessor. + // (2) At compile time, there is no way to determine whether the + // defining module has prespecialized metadata at a particular + // argument list. + // (3) Subsequent versions of the defining module may add or remove + // prespecialized metadata. + // + // To account for that, we only allow non-canonical prespecialization + // when at least one of the arguments is from the current module + // where non-canonical prespecialization might occur. Consequently, + // some prespecialization opportunities may be missed (such as when + // an argument comes from a module which it is known the defining + // module does not depend on). + !((canonicality == NoncanonicalSpecializedMetadata) && + !anyArgumentIsFromCurrentModule) && + IGM.getTypeInfoForUnlowered(type).isFixedSize( + ResilienceExpansion::Maximal); +} + +bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IRGenModule &IGM, NominalTypeDecl &nominal, CanType type, + SpecializedMetadataCanonicality canonicality) { if (isa(type) || isa(type)) { // TODO: On platforms without ObjC interop, we can do direct access to // class metadata. @@ -824,15 +914,15 @@ bool irgen:: // yet: // Struct> // Enum> - return isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGM, nominal, type, NotForUseOnlyFromAccessor); + return isSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, nominal, type, canonicality, NotForUseOnlyFromAccessor); } /// Is there a known address for canonical specialized metadata? The metadata /// there may need initialization before it is complete. -bool irgen::isInitializableTypeMetadataStaticallyAddressable(IRGenModule &IGM, - CanType type) { - if (isCompleteTypeMetadataStaticallyAddressable(IGM, type)) { +bool irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable( + IRGenModule &IGM, CanType type) { + if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) { // The address of the complete metadata is the address of the abstract // metadata. return true; @@ -844,16 +934,39 @@ bool irgen::isInitializableTypeMetadataStaticallyAddressable(IRGenModule &IGM, // the work of registering the class and its arguments with the ObjC // runtime. // Concretely, Clazz> can be prespecialized. - return isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGM, *nominal, type, ForUseOnlyFromAccessor); + return isSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, *nominal, type, CanonicalSpecializedMetadata, + ForUseOnlyFromAccessor); } return false; } +bool irgen::isNoncanonicalCompleteTypeMetadataStaticallyAddressable( + IRGenModule &IGM, CanType type) { + // If the canonical metadata record can be statically addressed, then there + // should be no visible non-canonical metadata record to address. + if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) { + return false; + } + + 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 isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, *nominalDecl, type, NoncanonicalSpecializedMetadata); + } + return false; +} + /// Is complete metadata for the given type available at a fixed address? -bool irgen::isCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, - CanType type) { +bool irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable( + IRGenModule &IGM, CanType type) { assert(!type->hasArchetype()); // Value type metadata only requires dynamic initialization on first @@ -867,8 +980,8 @@ bool irgen::isCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, return false; if (nominalDecl->isGenericContext()) - return isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGM, *nominalDecl, type); + return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, *nominalDecl, type, CanonicalSpecializedMetadata); auto expansion = ResilienceExpansion::Maximal; @@ -902,8 +1015,8 @@ bool irgen::isCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, if (isa(nominalDecl->getModuleScopeContext())) return false; - return isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGM, *nominalDecl, type); + return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, *nominalDecl, type, CanonicalSpecializedMetadata); } return false; @@ -926,17 +1039,21 @@ bool irgen::shouldCacheTypeMetadataAccess(IRGenModule &IGM, CanType type) { if (!hasKnownSwiftMetadata(IGM, classDecl)) return true; if (classDecl->isGenericContext() && - isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IGM, *classDecl, type, ForUseOnlyFromAccessor)) + isSpecializedNominalTypeMetadataStaticallyAddressable( + IGM, *classDecl, type, CanonicalSpecializedMetadata, + ForUseOnlyFromAccessor)) return false; auto strategy = IGM.getClassMetadataStrategy(classDecl); return strategy != ClassMetadataStrategy::Fixed; } // Trivially accessible metadata does not need a cache. - if (isCompleteTypeMetadataStaticallyAddressable(IGM, type)) + if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) return false; - + + if (isNoncanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) + return false; + return true; } @@ -1344,7 +1461,8 @@ namespace { auto flagsVal = FunctionTypeFlags() .withNumParameters(numParams) .withConvention(metadataConvention) - .withThrows(type->throws()) + .withAsync(type->isAsync()) + .withThrows(type->isThrowing()) .withParameterFlags(hasFlags) .withEscaping(isEscaping) .withDifferentiabilityKind( @@ -2351,7 +2469,7 @@ emitDirectTypeMetadataAccessFunctionBody(IRGenFunction &IGF, } // We should not be doing more serious work along this path. - assert(isCompleteTypeMetadataStaticallyAddressable(IGF.IGM, type)); + assert(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGF.IGM, type)); // Okay, everything else is built from a Swift metadata object. llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type); diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index 1bef5ea3f45cd..17090732971ae 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -500,32 +500,55 @@ static inline bool isAccessorLazilyGenerated(MetadataAccessStrategy strategy) { llvm_unreachable("bad kind"); } -/// Is complete metadata for the given type available at a fixed address? -bool isCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, CanType type); +/// Is non-canonical complete metadata for the given type available at a fixed +/// address? +bool isNoncanonicalCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, + CanType type); +/// Is canonical complete metadata for the given type available at a fixed +/// address? +bool isCanonicalCompleteTypeMetadataStaticallyAddressable(IRGenModule &IGM, + CanType type); /// Should requests for the given type's metadata be cached? bool shouldCacheTypeMetadataAccess(IRGenModule &IGM, CanType type); -bool isInitializableTypeMetadataStaticallyAddressable(IRGenModule &IGM, - CanType type); - -enum CanonicalSpecializedMetadataUsageIsOnlyFromAccessor : bool { +enum SpecializedMetadataUsageIsOnlyFromAccessor : bool { + /// The metadata must be accessed through an accessor function so that it can + /// be initialized. ForUseOnlyFromAccessor = true, + /// The metadata may be accessed directly. NotForUseOnlyFromAccessor = false }; -/// Is the address of the canonical specialization of a generic metadata -/// statically known? +enum SpecializedMetadataCanonicality : bool { + /// The metadata is canonical and can be used directly (subject to + /// initialization). + CanonicalSpecializedMetadata = true, + /// The metadata is not canonical and must be canonicalized before usage. + NoncanonicalSpecializedMetadata = false +}; + +/// Is the address of a specialization of the generic metadata statically known? /// -/// In other words, can a canonical specialization be formed for the specified -/// type. If onlyFromAccess is ForUseOnlyFromAccessor, then metadata's address -/// is known, but access to the metadata must go through the canonical -/// specialized accessor so that initialization of the metadata can occur. -bool isCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( +/// In other words, can a specialization be formed for the specified type. +/// +/// If onlyFromAccessor is ForUseOnlyFromAccessor, then metadata's address is +/// known, but access to the metadata must go through the canonical specialized +/// accessor so that initialization of the metadata can occur. +bool isSpecializedNominalTypeMetadataStaticallyAddressable( + IRGenModule &IGM, NominalTypeDecl &nominal, CanType type, + SpecializedMetadataCanonicality canonicality, + SpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor); + +/// Is the address of a specialization of the generic metadata which does not +/// require runtime initialization statically known? +bool isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( IRGenModule &IGM, NominalTypeDecl &nominal, CanType type, - CanonicalSpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor); + SpecializedMetadataCanonicality canonicality); -bool isCompleteCanonicalSpecializedNominalTypeMetadataStaticallyAddressable( - IRGenModule &IGM, NominalTypeDecl &nominal, CanType type); +/// Is the address of canonical metadata which may need to be initialized (e.g. +/// by registering it with the Objective-C runtime) statically known? +bool isCanonicalInitializableTypeMetadataStaticallyAddressable(IRGenModule &IGM, + CanType type); /// Determine how the given type metadata should be accessed. MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type); diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index 2728b1898e8ac..b49156177b8f1 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -30,6 +30,7 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" +#include "llvm/ExecutionEngine/Orc/DebugUtils.h" #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/IR/LLVMContext.h" @@ -75,6 +76,18 @@ static void *loadRuntimeLib(StringRef sharedLibName, return nullptr; } +static void DumpLLVMIR(const llvm::Module &M) { + std::string path = (M.getName() + ".ll").str(); + for (size_t count = 0; llvm::sys::fs::exists(path); ) + path = (M.getName() + llvm::utostr(count++) + ".ll").str(); + + std::error_code error; + llvm::raw_fd_ostream stream(path, error); + if (error) + return; + M.print(stream, /*AssemblyAnnotationWriter=*/nullptr); +} + void *swift::immediate::loadSwiftRuntime(ArrayRef runtimeLibPaths) { #if defined(_WIN32) @@ -196,20 +209,29 @@ int swift::RunImmediately(CompilerInstance &CI, const IRGenOptions &IRGenOpts, const SILOptions &SILOpts, std::unique_ptr &&SM) { + // TODO: Use OptimizedIRRequest for this. ASTContext &Context = CI.getASTContext(); // IRGen the main module. auto *swiftModule = CI.getMainModule(); const auto PSPs = CI.getPrimarySpecificPathsForAtMostOnePrimary(); + const auto &TBDOpts = CI.getInvocation().getTBDGenOptions(); auto GenModule = performIRGeneration( - IRGenOpts, swiftModule, std::move(SM), swiftModule->getName().str(), - PSPs, ArrayRef()); + swiftModule, IRGenOpts, TBDOpts, std::move(SM), + swiftModule->getName().str(), PSPs, ArrayRef()); if (Context.hadError()) return -1; assert(GenModule && "Emitted no diagnostics but IR generation failed?"); + performLLVM(IRGenOpts, Context.Diags, /*diagMutex*/ nullptr, /*hash*/ nullptr, + GenModule.getModule(), GenModule.getTargetMachine(), + PSPs.OutputFilename, Context.Stats); + + if (Context.hadError()) + return -1; + // Load libSwiftCore to setup process arguments. // // This must be done here, before any library loading has been done, to avoid @@ -281,6 +303,18 @@ int swift::RunImmediately(CompilerInstance &CI, } auto Module = GenModule.getModule(); + + switch (IRGenOpts.DumpJIT) { + case JITDebugArtifact::None: + break; + case JITDebugArtifact::LLVMIR: + DumpLLVMIR(*Module); + break; + case JITDebugArtifact::Object: + JIT->getObjTransformLayer().setTransform(llvm::orc::DumpObjects()); + break; + } + { // Get a generator for the process symbols and attach it to the main // JITDylib. diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index caa333508999a..f8d1e756b2d7d 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -143,7 +143,7 @@ SymbolInfo index::getSymbolInfoForModule(ModuleEntity Mod) { info.SubKind = SymbolSubKind::None; info.Properties = SymbolPropertySet(); if (auto *D = Mod.getAsSwiftModule()) { - if (!D->isClangModule()) { + if (!D->isNonSwiftModule()) { info.Lang = SymbolLanguage::Swift; } else { info.Lang = SymbolLanguage::C; diff --git a/lib/LLVMPasses/LLVMMergeFunctions.cpp b/lib/LLVMPasses/LLVMMergeFunctions.cpp index da76bba87217f..51db1af2f4892 100644 --- a/lib/LLVMPasses/LLVMMergeFunctions.cpp +++ b/lib/LLVMPasses/LLVMMergeFunctions.cpp @@ -222,12 +222,6 @@ class SwiftMergeFunctions : public ModulePass { bool runOnModule(Module &M) override; private: - enum { - /// The maximum number of parameters added to a merged functions. This - /// roughly corresponds to the number of differing constants. - maxAddedParams = 4 - }; - struct FunctionEntry; /// Describes the set of functions which are considered as "equivalent" (i.e. @@ -359,7 +353,7 @@ class SwiftMergeFunctions : public ModulePass { } }; - using ParamInfos = SmallVector; + using ParamInfos = SmallVector; GlobalNumberState GlobalNumbers; @@ -398,14 +392,15 @@ class SwiftMergeFunctions : public ModulePass { FunctionInfo removeFuncWithMostParams(FunctionInfos &FInfos); - bool deriveParams(ParamInfos &Params, FunctionInfos &FInfos); + bool deriveParams(ParamInfos &Params, FunctionInfos &FInfos, + unsigned maxParams); bool numOperandsDiffer(FunctionInfos &FInfos); bool constsDiffer(const FunctionInfos &FInfos, unsigned OpIdx); bool tryMapToParameter(FunctionInfos &FInfos, unsigned OpIdx, - ParamInfos &Params); + ParamInfos &Params, unsigned maxParams); void mergeWithParams(const FunctionInfos &FInfos, ParamInfos &Params); @@ -524,17 +519,9 @@ static bool mayMergeCallsToFunction(Function &F) { return true; } -/// Returns true if function \p F is eligible for merging. -static bool isEligibleFunction(Function *F) { - if (F->isDeclaration()) - return false; - - if (F->hasAvailableExternallyLinkage()) - return false; - - if (F->getFunctionType()->isVarArg()) - return false; - +/// Returns the benefit, which is approximately the size of the function. +/// Return 0, if the function should not be merged. +static unsigned getBenefit(Function *F) { unsigned Benefit = 0; // We don't want to merge very small functions, because the overhead of @@ -545,7 +532,7 @@ static bool isEligibleFunction(Function *F) { if (CallBase *CB = dyn_cast(&I)) { Function *Callee = CB->getCalledFunction(); if (Callee && !mayMergeCallsToFunction(*Callee)) - return false; + return 0; if (!Callee || !Callee->isIntrinsic()) { Benefit += 5; continue; @@ -554,6 +541,21 @@ static bool isEligibleFunction(Function *F) { Benefit += 1; } } + return Benefit; +} + +/// Returns true if function \p F is eligible for merging. +static bool isEligibleFunction(Function *F) { + if (F->isDeclaration()) + return false; + + if (F->hasAvailableExternallyLinkage()) + return false; + + if (F->getFunctionType()->isVarArg()) + return false; + + unsigned Benefit = getBenefit(F); if (Benefit < FunctionMergeThreshold) return false; @@ -723,12 +725,17 @@ bool SwiftMergeFunctions::tryMergeEquivalenceClass(FunctionEntry *FirstInClass) bool Changed = false; int Try = 0; + unsigned Benefit = getBenefit(FirstInClass->F); + + // The bigger the function, the more parameters are allowed. + unsigned maxParams = std::max(4u, Benefit / 100); + // We need multiple tries if there are some functions in FInfos which differ // too much from the first function in FInfos. But we limit the number of // tries to a small number, because this is quadratic. while (FInfos.size() >= 2 && Try++ < 4) { ParamInfos Params; - bool Merged = deriveParams(Params, FInfos); + bool Merged = deriveParams(Params, FInfos, maxParams); if (Merged) { mergeWithParams(FInfos, Params); Changed = true; @@ -767,7 +774,8 @@ removeFuncWithMostParams(FunctionInfos &FInfos) { /// Returns true on success, i.e. the functions in \p FInfos can be merged with /// the parameters returned in \p Params. bool SwiftMergeFunctions::deriveParams(ParamInfos &Params, - FunctionInfos &FInfos) { + FunctionInfos &FInfos, + unsigned maxParams) { for (FunctionInfo &FI : FInfos) FI.init(); @@ -796,7 +804,7 @@ bool SwiftMergeFunctions::deriveParams(ParamInfos &Params, if (constsDiffer(FInfos, OpIdx)) { // This instruction has operands which differ in at least some // functions. So we need to parameterize it. - if (!tryMapToParameter(FInfos, OpIdx, Params)) { + if (!tryMapToParameter(FInfos, OpIdx, Params, maxParams)) { // We ran out of parameters. return false; } @@ -845,7 +853,8 @@ bool SwiftMergeFunctions::constsDiffer(const FunctionInfos &FInfos, /// Returns true if a parameter could be created or found without exceeding the /// maximum number of parameters. bool SwiftMergeFunctions::tryMapToParameter(FunctionInfos &FInfos, - unsigned OpIdx, ParamInfos &Params) { + unsigned OpIdx, ParamInfos &Params, + unsigned maxParams) { ParamInfo *Matching = nullptr; // Try to find an existing parameter which exactly matches the differing // operands of the current instruction. @@ -858,7 +867,7 @@ bool SwiftMergeFunctions::tryMapToParameter(FunctionInfos &FInfos, if (!Matching) { // We need a new parameter. // Check if we are within the limit. - if (Params.size() >= maxAddedParams) + if (Params.size() >= maxParams) return false; Params.resize(Params.size() + 1); diff --git a/lib/Parse/Confusables.cpp b/lib/Parse/Confusables.cpp index 98b8ef17ecda6..8bd3039bb9105 100644 --- a/lib/Parse/Confusables.cpp +++ b/lib/Parse/Confusables.cpp @@ -14,9 +14,22 @@ char swift::confusable::tryConvertConfusableCharacterToASCII(uint32_t codepoint) { switch (codepoint) { -#define CONFUSABLE(CONFUSABLE_POINT, BASEPOINT) \ - case CONFUSABLE_POINT: return BASEPOINT; +#define CONFUSABLE(CONFUSABLE_POINT, CONFUSABLE_NAME, BASE_POINT, BASE_NAME) \ + case CONFUSABLE_POINT: \ + return BASE_POINT; #include "swift/Parse/Confusables.def" default: return 0; } } + +std::pair +swift::confusable::getConfusableAndBaseCodepointNames(uint32_t codepoint) { + switch (codepoint) { +#define CONFUSABLE(CONFUSABLE_POINT, CONFUSABLE_NAME, BASE_POINT, BASE_NAME) \ + case CONFUSABLE_POINT: \ + return std::make_pair(CONFUSABLE_NAME, BASE_NAME); +#include "swift/Parse/Confusables.def" + default: + return std::make_pair("", ""); + } +} diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 97b69394f3a82..976b3b2aeee92 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -921,15 +921,15 @@ void Lexer::lexDollarIdent() { return formToken(tok::sil_dollar, tokStart); bool isAllDigits = true; - for (;; ++CurPtr) { + while (true) { if (isDigit(*CurPtr)) { - // continue - } else if (clang::isIdentifierHead(*CurPtr, /*dollar*/true)) { + ++CurPtr; + continue; + } else if (advanceIfValidContinuationOfIdentifier(CurPtr, BufferEnd)) { isAllDigits = false; - // continue - } else { - break; + continue; } + break; } if (CurPtr == tokStart + 1) { @@ -2097,8 +2097,9 @@ bool Lexer::lexUnknown(bool EmitDiagnosticsIfToken) { EncodeToUTF8(Codepoint, ConfusedChar); llvm::SmallString<1> ExpectedChar; ExpectedChar += ExpectedCodepoint; + auto charNames = confusable::getConfusableAndBaseCodepointNames(Codepoint); diagnose(CurPtr - 1, diag::lex_confusable_character, ConfusedChar, - ExpectedChar) + charNames.first, ExpectedChar, charNames.second) .fixItReplaceChars(getSourceLoc(CurPtr - 1), getSourceLoc(Tmp), ExpectedChar); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 8c2b0a42e0cd3..28f5e4c2989a9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3754,7 +3754,8 @@ Parser::parseDecl(ParseDeclOptions Flags, StaticSpelling, tryLoc, HasLetOrVarKeyword); StaticLoc = SourceLoc(); // we handled static if present. MayNeedOverrideCompletion = true; - if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) + if ((AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion()) + && isCodeCompletionFirstPass()) return; std::for_each(Entries.begin(), Entries.end(), Handler); if (auto *D = DeclResult.getPtrOrNull()) @@ -3801,7 +3802,8 @@ Parser::parseDecl(ParseDeclOptions Flags, llvm::SmallVector Entries; DeclParsingContext.setCreateSyntax(SyntaxKind::EnumCaseDecl); DeclResult = parseDeclEnumCase(Flags, Attributes, Entries); - if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) + if ((AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion()) && + isCodeCompletionFirstPass()) break; std::for_each(Entries.begin(), Entries.end(), Handler); if (auto *D = DeclResult.getPtrOrNull()) @@ -3845,7 +3847,8 @@ Parser::parseDecl(ParseDeclOptions Flags, DeclResult = parseDeclSubscript(StaticLoc, StaticSpelling, Flags, Attributes, Entries); StaticLoc = SourceLoc(); // we handled static if present. - if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass()) + if ((AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion()) && + isCodeCompletionFirstPass()) break; std::for_each(Entries.begin(), Entries.end(), Handler); MayNeedOverrideCompletion = true; @@ -4024,6 +4027,8 @@ Parser::parseDecl(ParseDeclOptions Flags, CodeCompletion->setAttrTargetDeclKind(DK); } DeclResult.setHasCodeCompletion(); + if (isCodeCompletionFirstPass()) + return DeclResult; } if (DeclResult.isNonNull()) { @@ -5047,10 +5052,10 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { TAD->setUnderlyingTypeRepr(UnderlyingTy.getPtrOrNull()); TAD->getAttrs() = Attributes; - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { ContextChange CC(*this, TAD); - Status |= parseFreestandingGenericWhereClause(TAD, genericParams, Flags); + Status |= parseFreestandingGenericWhereClause(TAD); } if (UnderlyingTy.isNull()) { @@ -5720,7 +5725,7 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, } else if (auto paren = dyn_cast(cur)) { primaryVarIsWellFormed = false; cur = paren->getSubPattern(); - } else if (auto var = dyn_cast(cur)) { + } else if (auto var = dyn_cast(cur)) { primaryVarIsWellFormed = false; cur = var->getSubPattern(); } else { @@ -6361,10 +6366,12 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, TypeRepr *FuncRetTy = nullptr; DeclName FullName; ParameterList *BodyParams; + SourceLoc asyncLoc; SourceLoc throwsLoc; bool rethrows; Status |= parseFunctionSignature(SimpleName, FullName, BodyParams, - DefaultArgs, throwsLoc, rethrows, FuncRetTy); + DefaultArgs, asyncLoc, throwsLoc, rethrows, + FuncRetTy); if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return Status; @@ -6375,6 +6382,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, // Create the decl for the func and add it to the parent scope. auto *FD = FuncDecl::create(Context, StaticLoc, StaticSpelling, FuncLoc, FullName, NameLoc, + /*Async=*/asyncLoc.isValid(), asyncLoc, /*Throws=*/throwsLoc.isValid(), throwsLoc, GenericParams, BodyParams, FuncRetTy, @@ -6388,11 +6396,11 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, } } - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { ContextChange CC(*this, FD); - Status |= parseFreestandingGenericWhereClause(FD, GenericParams, Flags); + Status |= parseFreestandingGenericWhereClause(FD); if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return Status; @@ -6645,10 +6653,9 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, diagnoseWhereClauseInGenericParamList(GenericParams); - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { - auto whereStatus = - parseFreestandingGenericWhereClause(ED, GenericParams, Flags); + auto whereStatus = parseFreestandingGenericWhereClause(ED); if (whereStatus.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return whereStatus; @@ -6926,10 +6933,9 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, diagnoseWhereClauseInGenericParamList(GenericParams); - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { - auto whereStatus = - parseFreestandingGenericWhereClause(SD, GenericParams, Flags); + auto whereStatus = parseFreestandingGenericWhereClause(SD); if (whereStatus.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return whereStatus; @@ -7039,10 +7045,9 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, diagnoseWhereClauseInGenericParamList(GenericParams); - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { - auto whereStatus = - parseFreestandingGenericWhereClause(CD, GenericParams, Flags); + auto whereStatus = parseFreestandingGenericWhereClause(CD); if (whereStatus.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return whereStatus; @@ -7282,12 +7287,11 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, DefaultArgs.setFunctionContext(Subscript, Subscript->getIndices()); - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { ContextChange CC(*this, Subscript); - Status |= parseFreestandingGenericWhereClause(Subscript, GenericParams, - Flags); + Status |= parseFreestandingGenericWhereClause(Subscript); if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return Status; @@ -7406,14 +7410,24 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { return nullptr; } - // Parse 'throws' or 'rethrows'. + // Parse 'async' / 'throws' / 'rethrows'. + SourceLoc asyncLoc; SourceLoc throwsLoc; - if (consumeIf(tok::kw_throws, throwsLoc)) { - // okay - } else if (consumeIf(tok::kw_rethrows, throwsLoc)) { + bool rethrows = false; + parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, &rethrows); + + if (rethrows) { Attributes.add(new (Context) RethrowsAttr(throwsLoc)); } + // Initializers cannot be 'async'. + // FIXME: We should be able to lift this restriction. + if (asyncLoc.isValid()) { + diagnose(asyncLoc, diag::async_init) + .fixItRemove(asyncLoc); + asyncLoc = SourceLoc(); + } + diagnoseWhereClauseInGenericParamList(GenericParams); DeclName FullName(Context, DeclBaseName::createConstructor(), namePieces); @@ -7425,11 +7439,11 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { CD->setImplicitlyUnwrappedOptional(IUO); CD->getAttrs() = Attributes; - // Parse a 'where' clause if present, adding it to our GenericParamList. + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { ContextChange(*this, CD); - Status |= parseFreestandingGenericWhereClause(CD, GenericParams, Flags); + Status |= parseFreestandingGenericWhereClause(CD); if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. return Status; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index a1fbbd7e24bf3..d7f77869d6723 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -122,23 +122,25 @@ ParserResult Parser::parseExprAs() { /// parseExprArrow /// /// expr-arrow: -/// '->' -/// 'throws' '->' +/// 'async'? 'throws'? '->' ParserResult Parser::parseExprArrow() { - SourceLoc throwsLoc, arrowLoc; - if (Tok.is(tok::kw_throws)) { - throwsLoc = consumeToken(tok::kw_throws); - if (!Tok.is(tok::arrow)) { - diagnose(throwsLoc, diag::throws_in_wrong_position); - return nullptr; - } + SourceLoc asyncLoc, throwsLoc, arrowLoc; + + parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, /*rethrows=*/nullptr); + + if (Tok.isNot(tok::arrow)) { + assert(throwsLoc.isValid() || asyncLoc.isValid()); + diagnose(throwsLoc.isValid() ? throwsLoc : asyncLoc, + diag::async_or_throws_in_wrong_position, + throwsLoc.isValid() ? 0 : 2); + return nullptr; } + arrowLoc = consumeToken(tok::arrow); - if (Tok.is(tok::kw_throws)) { - diagnose(Tok.getLoc(), diag::throws_in_wrong_position); - throwsLoc = consumeToken(tok::kw_throws); - } - auto arrow = new (Context) ArrowExpr(throwsLoc, arrowLoc); + + parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, /*rethrows=*/nullptr); + + auto arrow = new (Context) ArrowExpr(asyncLoc, throwsLoc, arrowLoc); return makeParserResult(arrow); } @@ -325,6 +327,17 @@ ParserResult Parser::parseExprSequence(Diag<> Message, goto parse_operator; } + case tok::identifier: { + // 'async' followed by 'throws' or '->' implies that we have an arrow + // expression. + if (!(Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async") && + peekToken().isAny(tok::arrow, tok::kw_throws))) + goto done; + + LLVM_FALLTHROUGH; + } + case tok::arrow: case tok::kw_throws: { SyntaxParsingContext ArrowContext(SyntaxContext, SyntaxKind::ArrowExpr); @@ -366,9 +379,10 @@ ParserResult Parser::parseExprSequence(Diag<> Message, /// parseExprSequenceElement /// /// expr-sequence-element(Mode): -/// 'try' expr-unary(Mode) -/// 'try' '?' expr-unary(Mode) -/// 'try' '!' expr-unary(Mode) +/// '__await' expr-sequence-element(Mode) +/// 'try' expr-sequence-element(Mode) +/// 'try' '?' expr-sequence-element(Mode) +/// 'try' '!' expr-sequence-element(Mode) /// expr-unary(Mode) /// /// 'try' is not actually allowed at an arbitrary position of a @@ -377,6 +391,19 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, bool isExprBasic) { SyntaxParsingContext ElementContext(SyntaxContext, SyntaxContextKind::Expr); + + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.is(tok::kw___await)) { + SourceLoc awaitLoc = consumeToken(tok::kw___await); + ParserResult sub = parseExprUnary(message, isExprBasic); + if (!sub.hasCodeCompletion() && !sub.isNull()) { + ElementContext.setCreateSyntax(SyntaxKind::AwaitExpr); + sub = makeParserResult(new (Context) AwaitExpr(awaitLoc, sub.get())); + } + + return sub; + } + SourceLoc tryLoc; bool hadTry = consumeIf(tok::kw_try, tryLoc); Optional trySuffix; @@ -1019,31 +1046,34 @@ static bool isValidTrailingClosure(bool isExprBasic, Parser &P){ /// Map magic literal tokens such as #file to their /// MagicIdentifierLiteralExpr kind. static MagicIdentifierLiteralExpr::Kind -getMagicIdentifierLiteralKind(tok Kind) { +getMagicIdentifierLiteralKind(tok Kind, const LangOptions &Opts) { switch (Kind) { - case tok::kw___COLUMN__: - case tok::pound_column: - return MagicIdentifierLiteralExpr::Kind::Column; - case tok::kw___FILE__: case tok::pound_file: - return MagicIdentifierLiteralExpr::Kind::File; - case tok::pound_filePath: - return MagicIdentifierLiteralExpr::Kind::FilePath; - case tok::kw___FUNCTION__: - case tok::pound_function: - return MagicIdentifierLiteralExpr::Kind::Function; - case tok::kw___LINE__: - case tok::pound_line: - return MagicIdentifierLiteralExpr::Kind::Line; - case tok::kw___DSO_HANDLE__: - case tok::pound_dsohandle: - return MagicIdentifierLiteralExpr::Kind::DSOHandle; - + // TODO: Enable by default at the next source break. (SR-13199) + return Opts.EnableConcisePoundFile + ? MagicIdentifierLiteralExpr::FileIDSpelledAsFile + : MagicIdentifierLiteralExpr::FilePathSpelledAsFile; +#define MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) \ + case tok::TOKEN: \ + return MagicIdentifierLiteralExpr::Kind::NAME; +#include "swift/AST/MagicIdentifierKinds.def" default: llvm_unreachable("not a magic literal"); } } +/// Map magic literal kinds such as #file to their SyntaxKind. +static SyntaxKind +getMagicIdentifierSyntaxKind(MagicIdentifierLiteralExpr::Kind LiteralKind) { + switch (LiteralKind) { +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: \ + return SyntaxKind::SYNTAX_KIND; +#include "swift/AST/MagicIdentifierKinds.def" + } + llvm_unreachable("not a magic literal kind"); +} + ParserResult Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, bool periodHasKeyPathBehavior, @@ -1448,20 +1478,12 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { BooleanLiteralExpr(isTrue, consumeToken())); } - case tok::kw___FILE__: - case tok::kw___LINE__: - case tok::kw___COLUMN__: - case tok::kw___FUNCTION__: - case tok::kw___DSO_HANDLE__: { - StringRef replacement = ""; - switch (Tok.getKind()) { - default: llvm_unreachable("can't get here"); - case tok::kw___FILE__: replacement = "#file"; break; - case tok::kw___LINE__: replacement = "#line"; break; - case tok::kw___COLUMN__: replacement = "#column"; break; - case tok::kw___FUNCTION__: replacement = "#function"; break; - case tok::kw___DSO_HANDLE__: replacement = "#dsohandle"; break; - } + // Cases for deprecated magic identifier tokens +#define MAGIC_IDENTIFIER_DEPRECATED_TOKEN(NAME, TOKEN) case tok::TOKEN: +#include "swift/AST/MagicIdentifierKinds.def" + { + auto Kind = getMagicIdentifierLiteralKind(Tok.getKind(), Context.LangOpts); + auto replacement = MagicIdentifierLiteralExpr::getKindString(Kind); diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), replacement) @@ -1469,26 +1491,18 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { LLVM_FALLTHROUGH; } - case tok::pound_column: + // Cases for non-deprecated magic identifier tokens case tok::pound_file: - case tok::pound_filePath: - case tok::pound_function: - case tok::pound_line: - case tok::pound_dsohandle: { - SyntaxKind SKind = SyntaxKind::UnknownExpr; - switch (Tok.getKind()) { - case tok::pound_column: SKind = SyntaxKind::PoundColumnExpr; break; - case tok::pound_file: SKind = SyntaxKind::PoundFileExpr; break; - case tok::pound_filePath: SKind = SyntaxKind::PoundFilePathExpr; break; - case tok::pound_function: SKind = SyntaxKind::PoundFunctionExpr; break; - // FIXME: #line was renamed to #sourceLocation - case tok::pound_line: SKind = SyntaxKind::PoundLineExpr; break; - case tok::pound_dsohandle: SKind = SyntaxKind::PoundDsohandleExpr; break; - default: break; - } +#define MAGIC_IDENTIFIER_DEPRECATED_TOKEN(NAME, TOKEN) +#define MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) case tok::TOKEN: +#include "swift/AST/MagicIdentifierKinds.def" + { + auto Kind = getMagicIdentifierLiteralKind(Tok.getKind(), Context.LangOpts); + SyntaxKind SKind = getMagicIdentifierSyntaxKind(Kind); + ExprContext.setCreateSyntax(SKind); - auto Kind = getMagicIdentifierLiteralKind(Tok.getKind()); SourceLoc Loc = consumeToken(); + return makeParserResult(new (Context) MagicIdentifierLiteralExpr( Kind, Loc, /*implicit=*/false)); } @@ -3213,6 +3227,16 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, if (!Tok.is(tok::code_complete)) break; + // FIXME: Additional trailing closure completion on newline positions. + // let foo = SomeThing { + // ... + // } + // + // This was previously enabled, but it failed to suggest 'foo' because + // the token was considered a part of the initializer. + if (Tok.isAtStartOfLine()) + break; + // If the current completion mode doesn't support trailing closure // completion, leave the token here and let "postfix completion" to // handle it. diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index c534390f642e1..d174678924573 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -389,13 +389,9 @@ ParserStatus Parser::parseGenericWhereClause( } -/// Parse a free-standing where clause attached to a declaration, -/// adding it to a generic parameter list, if any, or to the given -/// generic context representing the declaration. +/// Parse a free-standing where clause attached to a declaration. ParserStatus Parser:: -parseFreestandingGenericWhereClause(GenericContext *genCtx, - GenericParamList *&genericParams, - ParseDeclOptions flags) { +parseFreestandingGenericWhereClause(GenericContext *genCtx) { assert(Tok.is(tok::kw_where) && "Shouldn't call this without a where"); SmallVector Requirements; @@ -406,20 +402,8 @@ parseFreestandingGenericWhereClause(GenericContext *genCtx, if (result.shouldStopParsing() || Requirements.empty()) return result; - if (genericParams) { - // Push the generic arguments back into a local scope so that references will - // find them. - Scope S(this, ScopeKind::Generics); - for (auto pd : genericParams->getParams()) - addToScope(pd); - - genericParams->addTrailingWhereClause(Context, WhereLoc, Requirements); - - } else { - // A where clause against outer generic parameters. - genCtx->setTrailingWhereClause( - TrailingWhereClause::create(Context, WhereLoc, Requirements)); - } + genCtx->setTrailingWhereClause( + TrailingWhereClause::create(Context, WhereLoc, Requirements)); return ParserStatus(); } diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index bf95b7375d0e7..06b75b7c2df5f 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -44,18 +44,9 @@ static DefaultArgumentKind getDefaultArgKind(Expr *init) { return DefaultArgumentKind::Normal; switch (magic->getKind()) { - case MagicIdentifierLiteralExpr::Column: - return DefaultArgumentKind::Column; - case MagicIdentifierLiteralExpr::File: - return DefaultArgumentKind::File; - case MagicIdentifierLiteralExpr::FilePath: - return DefaultArgumentKind::FilePath; - case MagicIdentifierLiteralExpr::Line: - return DefaultArgumentKind::Line; - case MagicIdentifierLiteralExpr::Function: - return DefaultArgumentKind::Function; - case MagicIdentifierLiteralExpr::DSOHandle: - return DefaultArgumentKind::DSOHandle; +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: return DefaultArgumentKind::NAME; +#include "swift/AST/MagicIdentifierKinds.def" } llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch."); @@ -770,7 +761,7 @@ Parser::parseFunctionArguments(SmallVectorImpl &NamePieces, /// Parse a function definition signature. /// func-signature: -/// func-arguments func-throws? func-signature-result? +/// func-arguments 'async'? func-throws? func-signature-result? /// func-signature-result: /// '->' type /// @@ -780,6 +771,7 @@ Parser::parseFunctionSignature(Identifier SimpleName, DeclName &FullName, ParameterList *&bodyParams, DefaultArgumentInfo &defaultArgs, + SourceLoc &asyncLoc, SourceLoc &throwsLoc, bool &rethrows, TypeRepr *&retType) { @@ -792,42 +784,13 @@ Parser::parseFunctionSignature(Identifier SimpleName, Status |= parseFunctionArguments(NamePieces, bodyParams, paramContext, defaultArgs); FullName = DeclName(Context, SimpleName, NamePieces); - - // Check for the 'throws' keyword. - rethrows = false; - if (Tok.is(tok::kw_throws)) { - throwsLoc = consumeToken(); - } else if (Tok.is(tok::kw_rethrows)) { - throwsLoc = consumeToken(); - rethrows = true; - } else if (Tok.isAny(tok::kw_throw, tok::kw_try)) { - throwsLoc = consumeToken(); - diagnose(throwsLoc, diag::throw_in_function_type) - .fixItReplace(throwsLoc, "throws"); - } - - SourceLoc arrowLoc; - - auto diagnoseInvalidThrows = [&]() -> Optional { - if (throwsLoc.isValid()) - return None; - if (Tok.is(tok::kw_throws)) { - throwsLoc = consumeToken(); - } else if (Tok.is(tok::kw_rethrows)) { - throwsLoc = consumeToken(); - rethrows = true; - } - - if (!throwsLoc.isValid()) - return None; - - auto diag = rethrows ? diag::rethrows_in_wrong_position - : diag::throws_in_wrong_position; - return diagnose(Tok, diag); - }; + // Check for the 'async' and 'throws' keywords. + rethrows = false; + parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, &rethrows); // If there's a trailing arrow, parse the rest as the result type. + SourceLoc arrowLoc; if (Tok.isAny(tok::arrow, tok::colon)) { SyntaxParsingContext ReturnCtx(SyntaxContext, SyntaxKind::ReturnClause); if (!consumeIf(tok::arrow, arrowLoc)) { @@ -839,41 +802,72 @@ Parser::parseFunctionSignature(Identifier SimpleName, // Check for 'throws' and 'rethrows' after the arrow, but // before the type, and correct it. - if (auto diagOpt = diagnoseInvalidThrows()) { - assert(arrowLoc.isValid()); - assert(throwsLoc.isValid()); - (*diagOpt).fixItExchange(SourceRange(arrowLoc), - SourceRange(throwsLoc)); - } + parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, &rethrows); ParserResult ResultType = parseDeclResultType(diag::expected_type_function_result); - if (ResultType.hasCodeCompletion()) - return ResultType; retType = ResultType.getPtrOrNull(); - if (!retType) { - Status.setIsParseError(); + Status |= ResultType; + if (Status.isError()) return Status; - } + + // Check for 'throws' and 'rethrows' after the type and correct it. + parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, &rethrows); } else { // Otherwise, we leave retType null. retType = nullptr; } - // Check for 'throws' and 'rethrows' after the type and correct it. - if (auto diagOpt = diagnoseInvalidThrows()) { - assert(arrowLoc.isValid()); - assert(retType); - SourceLoc typeEndLoc = Lexer::getLocForEndOfToken(SourceMgr, - retType->getEndLoc()); - SourceLoc throwsEndLoc = Lexer::getLocForEndOfToken(SourceMgr, throwsLoc); - (*diagOpt).fixItInsert(arrowLoc, rethrows ? "rethrows " : "throws ") - .fixItRemoveChars(typeEndLoc, throwsEndLoc); - } - return Status; } +void Parser::parseAsyncThrows( + SourceLoc existingArrowLoc, SourceLoc &asyncLoc, SourceLoc &throwsLoc, + bool *rethrows) { + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) { + asyncLoc = consumeToken(); + + if (existingArrowLoc.isValid()) { + diagnose(asyncLoc, diag::async_or_throws_in_wrong_position, 2) + .fixItRemove(asyncLoc) + .fixItInsert(existingArrowLoc, "async "); + } + } + + if (Tok.isAny(tok::kw_throws, tok::kw_throw, tok::kw_try) || + (rethrows && Tok.is(tok::kw_rethrows))) { + // If we allowed parsing rethrows, record whether we did in fact parse it. + if (rethrows) + *rethrows = Tok.is(tok::kw_rethrows); + + // Replace 'throw' or 'try' with 'throws'. + if (Tok.isAny(tok::kw_throw, tok::kw_try)) { + diagnose(Tok, diag::throw_in_function_type) + .fixItReplace(Tok.getLoc(), "throws"); + } + + StringRef keyword = Tok.getText(); + throwsLoc = consumeToken(); + + if (existingArrowLoc.isValid()) { + diagnose(throwsLoc, diag::async_or_throws_in_wrong_position, + rethrows ? (*rethrows ? 1 : 0) : 0) + .fixItRemove(throwsLoc) + .fixItInsert(existingArrowLoc, (keyword + " ").str()); + } + + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) { + asyncLoc = consumeToken(); + + diagnose(asyncLoc, diag::async_after_throws, rethrows && *rethrows) + .fixItRemove(asyncLoc) + .fixItInsert( + existingArrowLoc.isValid() ? existingArrowLoc : throwsLoc, "async "); + } + } +} /// Parse a pattern with an optional type annotation. /// @@ -1022,8 +1016,8 @@ ParserResult Parser::parsePattern() { return makeParserCodeCompletionResult(); if (subPattern.isNull()) return nullptr; - return makeParserResult(new (Context) VarPattern(varLoc, isLet, - subPattern.get())); + return makeParserResult( + new (Context) BindingPattern(varLoc, isLet, subPattern.get())); } default: @@ -1233,7 +1227,7 @@ ParserResult Parser::parseMatchingPatternAsLetOrVar(bool isLet, ParserResult subPattern = parseMatchingPattern(isExprBasic); if (subPattern.isNull()) return nullptr; - auto *varP = new (Context) VarPattern(varLoc, isLet, subPattern.get()); + auto *varP = new (Context) BindingPattern(varLoc, isLet, subPattern.get()); return makeParserResult(ParserStatus(subPattern), varP); } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 893a61f48b548..a58f435d8dc1f 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -71,7 +71,7 @@ bool Parser::isStartOfStmt() { case tok::kw_try: { // "try" cannot actually start any statements, but we parse it there for - // better recovery. + // better recovery in cases like 'try return'. Parser::BacktrackingScope backtrack(*this); consumeToken(tok::kw_try); return isStartOfStmt(); @@ -658,7 +658,8 @@ ParserResult Parser::parseBraceItemList(Diag<> ID) { diagnose(Tok, ID); // Attempt to recover by looking for a left brace on the same line - if (!skipUntilTokenOrEndOfLine(tok::l_brace)) + if (!skipUntilTokenOrEndOfLine(tok::l_brace, tok::r_brace) || + !Tok.is(tok::l_brace)) return nullptr; } SyntaxParsingContext LocalContext(SyntaxContext, SyntaxKind::CodeBlock); @@ -949,6 +950,7 @@ ParserResult Parser::parseStmtDefer() { /*FuncLoc=*/ SourceLoc(), name, /*NameLoc=*/ PreviousLoc, + /*Async=*/ false, /*AsyncLoc=*/ SourceLoc(), /*Throws=*/ false, /*ThrowsLoc=*/ SourceLoc(), /*generic params*/ nullptr, params, @@ -1092,8 +1094,8 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, P.CurDeclContext); var->setImplicit(); auto namePattern = new (P.Context) NamedPattern(var); - auto varPattern = new (P.Context) VarPattern(loc, /*isLet*/true, - namePattern); + auto varPattern = + new (P.Context) BindingPattern(loc, /*isLet*/ true, namePattern); varPattern->setImplicit(); patternResult = makeParserResult(varPattern); } @@ -1485,8 +1487,8 @@ Parser::parseStmtConditionElement(SmallVectorImpl &result, ThePattern = parseMatchingPattern(/*isExprBasic*/ true); if (ThePattern.isNonNull()) { - auto *P = new (Context) VarPattern(IntroducerLoc, wasLet, - ThePattern.get()); + auto *P = + new (Context) BindingPattern(IntroducerLoc, wasLet, ThePattern.get()); ThePattern = makeParserResult(Status, P); } @@ -1699,7 +1701,8 @@ ParserResult Parser::parseStmtIf(LabeledStmtInfo LabelInfo, // got a problem. If the last bit is 'else ... {' on one line, let's // assume they've forgotten the 'if'. BacktrackingScope backtrack(*this); - implicitlyInsertIf = skipUntilTokenOrEndOfLine(tok::l_brace); + if (skipUntilTokenOrEndOfLine(tok::l_brace, tok::r_brace)) + implicitlyInsertIf = Tok.is(tok::l_brace); } if (Tok.is(tok::kw_if) || implicitlyInsertIf) { @@ -2464,7 +2467,6 @@ struct FallthroughFinder : ASTWalker { } bool walkToDeclPre(Decl *d) override { return false; } - bool walkToTypeLocPre(TypeLoc &tl) override { return false; } bool walkToTypeReprPre(TypeRepr *t) override { return false; } static FallthroughStmt *findFallthrough(Stmt *s) { diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index d4d20c0e36d6a..aefea2401c15a 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -354,7 +354,7 @@ ParserResult Parser::parseSILBoxType(GenericParamList *generics, /// attribute-list type-function /// /// type-function: -/// type-composition 'throws'? '->' type +/// type-composition 'async'? 'throws'? '->' type /// ParserResult Parser::parseType(Diag<> MessageID, bool HandleCodeCompletion, @@ -410,13 +410,22 @@ ParserResult Parser::parseType(Diag<> MessageID, auto tyR = ty.get(); auto status = ParserStatus(ty); + // Parse an async specifier. + SourceLoc asyncLoc; + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) { + asyncLoc = consumeToken(); + } + // Parse a throws specifier. - // Don't consume 'throws', if the next token is not '->', so we can emit a - // more useful diagnostic when parsing a function decl. + // Don't consume 'throws', if the next token is not '->' or 'async', so we + // can emit a more useful diagnostic when parsing a function decl. SourceLoc throwsLoc; if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw, tok::kw_try) && - peekToken().is(tok::arrow)) { - if (Tok.isNot(tok::kw_throws)) { + (peekToken().is(tok::arrow) || + (Context.LangOpts.EnableExperimentalConcurrency && + peekToken().isContextualKeyword("async")))) { + if (Tok.isAny(tok::kw_rethrows, tok::kw_throw, tok::kw_try)) { // 'rethrows' is only allowed on function declarations for now. // 'throw' or 'try' are probably typos for 'throws'. Diag<> DiagID = Tok.is(tok::kw_rethrows) ? @@ -425,18 +434,25 @@ ParserResult Parser::parseType(Diag<> MessageID, .fixItReplace(Tok.getLoc(), "throws"); } throwsLoc = consumeToken(); + + // 'async' must preceed 'throws'; accept this but complain. + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) { + asyncLoc = consumeToken(); + + diagnose(asyncLoc, diag::async_after_throws, false) + .fixItRemove(asyncLoc) + .fixItInsert(throwsLoc, "async "); + } } if (Tok.is(tok::arrow)) { // Handle type-function if we have an arrow. SourceLoc arrowLoc = consumeToken(); - if (Tok.is(tok::kw_throws)) { - Diag<> DiagID = diag::throws_in_wrong_position; - diagnose(Tok.getLoc(), DiagID) - .fixItInsert(arrowLoc, "throws ") - .fixItRemove(Tok.getLoc()); - throwsLoc = consumeToken(); - } + + // Handle async/throws in the wrong place. + parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, /*rethrows=*/nullptr); + ParserResult SecondHalf = parseType(diag::expected_type_function_result); if (SecondHalf.isNull()) { @@ -451,6 +467,8 @@ ParserResult Parser::parseType(Diag<> MessageID, Builder.useArrow(SyntaxContext->popToken()); if (throwsLoc.isValid()) Builder.useThrowsOrRethrowsKeyword(SyntaxContext->popToken()); + if (asyncLoc.isValid()) + Builder.useAsyncKeyword(SyntaxContext->popToken()); auto InputNode(std::move(*SyntaxContext->popIf())); if (auto TupleTypeNode = InputNode.getAs()) { @@ -560,8 +578,8 @@ ParserResult Parser::parseType(Diag<> MessageID, } } - tyR = new (Context) FunctionTypeRepr(generics, argsTyR, throwsLoc, arrowLoc, - SecondHalf.get(), + tyR = new (Context) FunctionTypeRepr(generics, argsTyR, asyncLoc, throwsLoc, + arrowLoc, SecondHalf.get(), patternGenerics, patternSubsTypes, invocationSubsTypes); } else if (auto firstGenerics = generics ? generics : patternGenerics) { @@ -1569,12 +1587,31 @@ bool Parser::canParseType() { } break; } - + + // Handle type-function if we have an 'async'. + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) { + consumeToken(); + + // 'async' isn't a valid type without being followed by throws/rethrows + // or a return. + if (!Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::arrow)) + return false; + } + // Handle type-function if we have an arrow or 'throws'/'rethrows' modifier. if (Tok.isAny(tok::kw_throws, tok::kw_rethrows)) { consumeToken(); + + // Allow 'async' here even though it is ill-formed, so we can provide + // a better error. + if (Context.LangOpts.EnableExperimentalConcurrency && + Tok.isContextualKeyword("async")) + consumeToken(); + // "throws" or "rethrows" isn't a valid type without being followed by - // a return. + // a return. We also accept 'async' here so we can provide a better + // error. if (!Tok.is(tok::arrow)) return false; } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 92dc540964995..601527fa7ba19 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -24,7 +24,6 @@ #include "swift/AST/SourceFile.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" -#include "swift/Basic/Timer.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/ParseSILSupport.h" @@ -661,7 +660,7 @@ void Parser::skipSingle() { switch (Tok.getKind()) { case tok::l_paren: consumeToken(); - skipUntil(tok::r_paren); + skipUntil(tok::r_paren, tok::r_brace); consumeIf(tok::r_paren); break; case tok::l_brace: @@ -671,7 +670,7 @@ void Parser::skipSingle() { break; case tok::l_square: consumeToken(); - skipUntil(tok::r_square); + skipUntil(tok::r_square, tok::r_brace); consumeIf(tok::r_square); break; case tok::pound_if: @@ -826,11 +825,11 @@ void Parser::skipUntilConditionalBlockClose() { } } -bool Parser::skipUntilTokenOrEndOfLine(tok T1) { - while (Tok.isNot(tok::eof, T1) && !Tok.isAtStartOfLine()) +bool Parser::skipUntilTokenOrEndOfLine(tok T1, tok T2) { + while (Tok.isNot(tok::eof, T1, T2) && !Tok.isAtStartOfLine()) skipSingle(); - return Tok.is(T1) && !Tok.isAtStartOfLine(); + return Tok.isAny(T1, T2) && !Tok.isAtStartOfLine(); } bool Parser::loadCurrentSyntaxNodeFromCache() { diff --git a/lib/SIL/IR/Bridging.cpp b/lib/SIL/IR/Bridging.cpp index c5760f1805eda..053a00de71c7a 100644 --- a/lib/SIL/IR/Bridging.cpp +++ b/lib/SIL/IR/Bridging.cpp @@ -205,9 +205,12 @@ Type TypeConverter::getLoweredCBridgedType(AbstractionPattern pattern, bridging, /*non-optional*/false); - return FunctionType::get(newParams, newResult, - funTy->getExtInfo().withSILRepresentation( - SILFunctionType::Representation::Block)); + return FunctionType::get( + newParams, newResult, + funTy->getExtInfo() + .intoBuilder() + .withSILRepresentation(SILFunctionType::Representation::Block) + .build()); } } } diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 26aecc97afdf5..7f928667b8913 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -1013,6 +1013,7 @@ ANY_OWNERSHIP_BUILTIN(Swift3ImplicitObjCEntrypoint) ANY_OWNERSHIP_BUILTIN(PoundAssert) ANY_OWNERSHIP_BUILTIN(GlobalStringTablePointer) ANY_OWNERSHIP_BUILTIN(TypePtrAuthDiscriminator) +ANY_OWNERSHIP_BUILTIN(IntInstrprofIncrement) #undef ANY_OWNERSHIP_BUILTIN // This is correct today since we do not have any builtins which return diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index 037bfc9b3e6ec..c925cc83bb737 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -62,11 +62,14 @@ SILType SILBuilder::getPartialApplyResultType( auto params = FTI->getParameters(); auto newParams = params.slice(0, params.size() - argCount); - auto extInfo = FTI->getExtInfo() - .withRepresentation(SILFunctionType::Representation::Thick) - .withIsPseudogeneric(false); + auto extInfoBuilder = + FTI->getExtInfo() + .intoBuilder() + .withRepresentation(SILFunctionType::Representation::Thick) + .withIsPseudogeneric(false); if (onStack) - extInfo = extInfo.withNoEscape(); + extInfoBuilder = extInfoBuilder.withNoEscape(); + auto extInfo = extInfoBuilder.build(); // If the original method has an @unowned_inner_pointer return, the partial // application thunk will lifetime-extend 'self' for us, converting the diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 9835c2865adba..42c19adb7b30e 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -11,20 +11,22 @@ //===----------------------------------------------------------------------===// #include "swift/SIL/SILDeclRef.h" -#include "swift/SIL/SILLocation.h" -#include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILLinkage.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/raw_ostream.h" +#include "swift/SIL/SILLocation.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Mangle.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" using namespace swift; /// Get the method dispatch mechanism for a method. diff --git a/lib/SIL/IR/SILDefaultWitnessTable.cpp b/lib/SIL/IR/SILDefaultWitnessTable.cpp index 40688ba9f4246..7c427f5ba6f89 100644 --- a/lib/SIL/IR/SILDefaultWitnessTable.cpp +++ b/lib/SIL/IR/SILDefaultWitnessTable.cpp @@ -97,7 +97,8 @@ convertToDefinition(ArrayRef entries) { std::string SILDefaultWitnessTable::getUniqueName() const { Mangle::ASTMangler Mangler; - return Mangler.mangleTypeWithoutPrefix(getProtocol()->getDeclaredType()); + return Mangler.mangleTypeWithoutPrefix( + getProtocol()->getDeclaredInterfaceType()); } SILDefaultWitnessTable::~SILDefaultWitnessTable() { diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index c31d6e1fb7340..f5e27bbd56c1b 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -107,7 +107,7 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, ExactSelfClass(isExactSelfClass), Inlined(false), Zombie(false), HasOwnership(true), WasDeserializedCanonical(false), IsWithoutActuallyEscapingThunk(false), - OptMode(unsigned(OptimizationMode::NotSet)), + IsAsync(false), OptMode(unsigned(OptimizationMode::NotSet)), EffectsKindAttr(unsigned(E)) { assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index b56d34ea74d00..c28ba05090b1b 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -217,6 +217,10 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( } addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration, constant); + + if (auto *funcDecl = dyn_cast(decl)) { + F->setAsync(funcDecl->hasAsync()); + } } return F; diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index b18402a549b7c..60f7584418787 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -235,8 +235,25 @@ IndexSubset *SILFunctionType::getDifferentiabilityResultIndices() { resultIndices.push_back(resultAndIndex.index()); // Check `inout` parameters. for (auto inoutParamAndIndex : enumerate(getIndirectMutatingParameters())) - if (inoutParamAndIndex.value().getDifferentiability() != - SILParameterDifferentiability::NotDifferentiable) + // FIXME(TF-1305): The `getResults().empty()` condition is a hack. + // + // Currently, an `inout` parameter can either be: + // 1. Both a differentiability parameter and a differentiability result. + // 2. `@noDerivative`: neither a differentiability parameter nor a + // differentiability result. + // However, there is no way to represent an `inout` parameter that: + // 3. Is a differentiability result but not a differentiability parameter. + // 4. Is a differentiability parameter but not a differentiability result. + // This case is not currently expressible and does not yet have clear use + // cases, so supporting it is a non-goal. + // + // See TF-1305 for solution ideas. For now, `@noDerivative` `inout` + // parameters are not treated as differentiability results, unless the + // original function has no formal results, in which case all `inout` + // parameters are treated as differentiability results. + if (getResults().empty() || + inoutParamAndIndex.value().getDifferentiability() != + SILParameterDifferentiability::NotDifferentiable) resultIndices.push_back(getNumResults() + inoutParamAndIndex.index()); auto numSemanticResults = getNumResults() + getNumIndirectMutatingParameters(); @@ -268,7 +285,8 @@ SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, ? SILResultDifferentiability::DifferentiableOrNotApplicable : SILResultDifferentiability::NotDifferentiable)); } - auto newExtInfo = getExtInfo().withDifferentiabilityKind(kind); + auto newExtInfo = + getExtInfo().intoBuilder().withDifferentiabilityKind(kind).build(); return get(getInvocationGenericSignature(), newExtInfo, getCoroutineKind(), getCalleeConvention(), newParameters, getYields(), newResults, getOptionalErrorResult(), getPatternSubstitutions(), @@ -279,8 +297,11 @@ SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { if (!isDifferentiable()) return CanSILFunctionType(this); - auto nondiffExtInfo = getExtInfo().withDifferentiabilityKind( - DifferentiabilityKind::NonDifferentiable); + auto nondiffExtInfo = + getExtInfo() + .intoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::NonDifferentiable) + .build(); SmallVector newParams; for (auto ¶m : getParameters()) newParams.push_back(param.getWithDifferentiability( @@ -428,8 +449,9 @@ static CanSILFunctionType getAutoDiffDifferentialType( } } SmallVector differentialResults; - if (inoutParamIndices->isEmpty()) { - for (auto resultIndex : resultIndices->getIndices()) { + for (auto resultIndex : resultIndices->getIndices()) { + // Handle formal original result. + if (resultIndex < originalFnTy->getNumResults()) { auto &result = originalResults[resultIndex]; auto resultTan = result.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); @@ -448,8 +470,27 @@ static CanSILFunctionType getAutoDiffDifferentialType( substReplacements.push_back(resultTanType); differentialResults.push_back({gpType, resultConv}); } + continue; } + // Handle original `inout` parameter. + auto inoutParamIndex = resultIndex - originalFnTy->getNumResults(); + auto inoutParamIt = std::next( + originalFnTy->getIndirectMutatingParameters().begin(), inoutParamIndex); + auto paramIndex = + std::distance(originalFnTy->getParameters().begin(), &*inoutParamIt); + // If the original `inout` parameter is a differentiability parameter, then + // it already has a corresponding differential parameter. Skip adding a + // corresponding differential result. + if (parameterIndices->contains(paramIndex)) + continue; + auto inoutParam = originalFnTy->getParameters()[paramIndex]; + auto paramTan = inoutParam.getInterfaceType()->getAutoDiffTangentSpace( + lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + differentialResults.push_back( + {paramTan->getCanonicalType(), ResultConvention::Indirect}); } + SubstitutionMap substitutions; if (!substGenericParams.empty()) { auto genericSig = @@ -710,7 +751,9 @@ CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( CanGenericSignature derivativeFnInvocationGenSig, bool isReabstractionThunk) { assert(parameterIndices); + assert(!parameterIndices->isEmpty() && "Parameter indices must not be empty"); assert(resultIndices); + assert(!resultIndices->isEmpty() && "Result indices must not be empty"); auto &ctx = getASTContext(); // Look up result in cache. @@ -928,7 +971,7 @@ static CanType getKnownType(Optional &cacheSlot, ASTContext &C, CanAnyFunctionType Lowering::adjustFunctionType(CanAnyFunctionType t, AnyFunctionType::ExtInfo extInfo) { - if (t->getExtInfo() == extInfo) + if (t->getExtInfo().isEqualTo(extInfo, useClangTypes(t))) return t; return CanAnyFunctionType(t->withExtInfo(extInfo)); } @@ -939,7 +982,8 @@ Lowering::adjustFunctionType(CanSILFunctionType type, SILFunctionType::ExtInfo extInfo, ParameterConvention callee, ProtocolConformanceRef witnessMethodConformance) { - if (type->getExtInfo() == extInfo && type->getCalleeConvention() == callee && + if (type->getExtInfo().isEqualTo(extInfo, useClangTypes(type)) && + type->getCalleeConvention() == callee && type->getWitnessMethodConformanceOrInvalid() == witnessMethodConformance) return type; @@ -961,7 +1005,7 @@ SILFunctionType::getWithRepresentation(Representation repr) { CanSILFunctionType SILFunctionType::getWithExtInfo(ExtInfo newExt) { auto oldExt = getExtInfo(); - if (newExt == oldExt) + if (newExt.isEqualTo(oldExt, useClangTypes(this))) return CanSILFunctionType(this); auto calleeConvention = @@ -1177,8 +1221,8 @@ class SubstFunctionTypeCollector { for (unsigned i : indices(upperBoundConformances)) { auto proto = upperBoundConformances[i]; auto conformance = substTypeConformances[i]; - substRequirements.push_back(Requirement(RequirementKind::Conformance, - param, proto->getDeclaredType())); + substRequirements.push_back(Requirement(RequirementKind::Conformance, param, + proto->getDeclaredInterfaceType())); substConformances.push_back(conformance); } @@ -2011,9 +2055,10 @@ static CanSILFunctionType getSILFunctionType( // Map 'throws' to the appropriate error convention. Optional errorResult; - assert((!foreignInfo.Error || substFnInterfaceType->getExtInfo().throws()) && - "foreignError was set but function type does not throw?"); - if (substFnInterfaceType->getExtInfo().throws() && !foreignInfo.Error) { + assert( + (!foreignInfo.Error || substFnInterfaceType->getExtInfo().isThrowing()) && + "foreignError was set but function type does not throw?"); + if (substFnInterfaceType->getExtInfo().isThrowing() && !foreignInfo.Error) { assert(!origType.isForeign() && "using native Swift error convention for foreign type!"); SILType exnType = SILType::getExceptionType(TC.Context); @@ -2106,11 +2151,13 @@ static CanSILFunctionType getSILFunctionType( // NOTE: SILFunctionType::ExtInfo doesn't track everything that // AnyFunctionType::ExtInfo tracks. For example: 'throws' or 'auto-closure' - auto silExtInfo = SILFunctionType::ExtInfo() - .withRepresentation(extInfo.getSILRepresentation()) - .withIsPseudogeneric(pseudogeneric) - .withNoEscape(extInfo.isNoEscape()) - .withDifferentiabilityKind(extInfo.getDifferentiabilityKind()); + auto silExtInfo = + SILFunctionType::ExtInfoBuilder() + .withRepresentation(extInfo.getSILRepresentation()) + .withIsPseudogeneric(pseudogeneric) + .withNoEscape(extInfo.isNoEscape()) + .withDifferentiabilityKind(extInfo.getDifferentiabilityKind()) + .build(); // Build the substituted generic signature we extracted. SubstitutionMap substitutions; @@ -3716,7 +3763,7 @@ class SILTypeSubstituter : // pseudogeneric. auto extInfo = origType->getExtInfo(); if (!shouldSubstituteOpaqueArchetypes) - extInfo = extInfo.withIsPseudogeneric(false); + extInfo = extInfo.intoBuilder().withIsPseudogeneric(false).build(); auto genericSig = shouldSubstituteOpaqueArchetypes ? origType->getInvocationGenericSignature() @@ -3962,7 +4009,7 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, case SILFunctionTypeRepresentation::Closure: case SILFunctionTypeRepresentation::WitnessMethod: { // No bridging needed for native functions. - if (t->getExtInfo() == extInfo) + if (t->getExtInfo().isEqualTo(extInfo, useClangTypes(t))) return t; return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), extInfo); @@ -4171,7 +4218,7 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, bridgingFnPattern.rewriteType(genericSig, curried); // Build the uncurried function type. - if (innerExtInfo.throws()) + if (innerExtInfo.isThrowing()) extInfo = extInfo.withThrows(true); bridgedParams.push_back(selfParam); diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 4a6b02cd42bd1..609a8318b153d 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -157,7 +157,6 @@ bool SILGlobalVariable::isValidStaticInitializerInst(const SILInstruction *I, switch (cast(I)->getEncoding()) { case StringLiteralInst::Encoding::Bytes: case StringLiteralInst::Encoding::UTF8: - case StringLiteralInst::Encoding::UTF16: return true; case StringLiteralInst::Encoding::ObjCSelector: // Objective-C selector string literals cannot be used in static @@ -220,9 +219,9 @@ SILGlobalVariable *swift::getVariableOfGlobalInit(SILFunction *AddrF) { // and the globalinit_func is called by "once" from a single location, // continue; otherwise bail. BuiltinInst *CallToOnce; - auto *InitF = findInitializer(&AddrF->getModule(), AddrF, CallToOnce); + auto *InitF = findInitializer(AddrF, CallToOnce); - if (!InitF || !InitF->getName().startswith("globalinit_")) + if (!InitF) return nullptr; // If the globalinit_func is trivial, continue; otherwise bail. @@ -247,8 +246,8 @@ SILFunction *swift::getCalleeOfOnceCall(BuiltinInst *BI) { } // Find the globalinit_func by analyzing the body of the addressor. -SILFunction *swift::findInitializer(SILModule *Module, SILFunction *AddrF, - BuiltinInst *&CallToOnce) { +SILFunction *swift::findInitializer(SILFunction *AddrF, + BuiltinInst *&CallToOnce) { // We only handle a single SILBasicBlock for now. if (AddrF->size() != 1) return nullptr; @@ -272,7 +271,10 @@ SILFunction *swift::findInitializer(SILModule *Module, SILFunction *AddrF, } if (!CallToOnce) return nullptr; - return getCalleeOfOnceCall(CallToOnce); + SILFunction *callee = getCalleeOfOnceCall(CallToOnce); + if (!callee->getName().startswith("globalinit_")) + return nullptr; + return callee; } SILGlobalVariable * diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 85e21ea609f21..1faa9ec6ae6a1 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1043,9 +1043,6 @@ CondFailInst *CondFailInst::create(SILDebugLocation DebugLoc, SILValue Operand, } uint64_t StringLiteralInst::getCodeUnitCount() { - auto E = unsigned(Encoding::UTF16); - if (SILInstruction::Bits.StringLiteralInst.TheEncoding == E) - return unicode::getUTF16Length(getValue()); return SILInstruction::Bits.StringLiteralInst.Length; } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 2225c047abd3a..bb84c23ef1e4c 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1339,7 +1339,6 @@ class SILPrinter : public SILInstructionVisitor { switch (kind) { case StringLiteralInst::Encoding::Bytes: return "bytes "; case StringLiteralInst::Encoding::UTF8: return "utf8 "; - case StringLiteralInst::Encoding::UTF16: return "utf16 "; case StringLiteralInst::Encoding::ObjCSelector: return "objc_selector "; } llvm_unreachable("bad string literal encoding"); @@ -2599,6 +2598,9 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (isWithoutActuallyEscapingThunk()) OS << "[without_actually_escaping] "; + if (isAsync()) + OS << "[async] "; + switch (getSpecialPurpose()) { case SILFunction::Purpose::None: break; @@ -2918,12 +2920,10 @@ printSILCoverageMaps(SILPrintContext &Ctx, M->print(Ctx); } -using MagicFileStringMap = - llvm::StringMap>; +using FileIDMap = llvm::StringMap>; -static void -printMagicFileStringMapEntry(SILPrintContext &Ctx, - const MagicFileStringMap::MapEntryTy &entry) { +static void printFileIDMapEntry(SILPrintContext &Ctx, + const FileIDMap::MapEntryTy &entry) { auto &OS = Ctx.OS(); OS << "// '" << std::get<0>(entry.second) << "' => '" << entry.first() << "'"; @@ -2934,12 +2934,11 @@ printMagicFileStringMapEntry(SILPrintContext &Ctx, OS << "\n"; } -static void printMagicFileStringMap(SILPrintContext &Ctx, - const MagicFileStringMap map) { +static void printFileIDMap(SILPrintContext &Ctx, const FileIDMap map) { if (map.empty()) return; - Ctx.OS() << "\n\n// Mappings from '#file' to '#filePath':\n"; + Ctx.OS() << "\n\n// Mappings from '#fileID' to '#filePath':\n"; if (Ctx.sortSIL()) { llvm::SmallVector keys; @@ -2962,10 +2961,10 @@ static void printMagicFileStringMap(SILPrintContext &Ctx, }); for (auto key : keys) - printMagicFileStringMapEntry(Ctx, *map.find(key)); + printFileIDMapEntry(Ctx, *map.find(key)); } else { for (const auto &entry : map) - printMagicFileStringMapEntry(Ctx, entry); + printFileIDMapEntry(Ctx, entry); } } @@ -3084,8 +3083,8 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printExternallyVisibleDecls(PrintCtx, externallyVisible.getArrayRef()); if (M) - printMagicFileStringMap( - PrintCtx, M->computeMagicFileStringMap(/*shouldDiagnose=*/false)); + printFileIDMap( + PrintCtx, M->computeFileIDMap(/*shouldDiagnose=*/false)); OS << "\n\n"; } @@ -3106,52 +3105,56 @@ void SILInstruction::printInContext(llvm::raw_ostream &OS) const { SILPrinter(Ctx).printInContext(this); } +void SILVTableEntry::print(llvm::raw_ostream &OS) const { + getMethod().print(OS); + OS << ": "; + + PrintOptions QualifiedSILTypeOptions = PrintOptions::printQualifiedSILType(); + bool HasSingleImplementation = false; + switch (getMethod().kind) { + default: + break; + case SILDeclRef::Kind::IVarDestroyer: + case SILDeclRef::Kind::Destroyer: + case SILDeclRef::Kind::Deallocator: + HasSingleImplementation = true; + } + // No need to emit the signature for methods that may have only + // single implementation, e.g. for destructors. + if (!HasSingleImplementation) { + QualifiedSILTypeOptions.CurrentModule = + getMethod().getDecl()->getDeclContext()->getParentModule(); + getMethod().getDecl()->getInterfaceType().print( + OS, QualifiedSILTypeOptions); + OS << " : "; + } + OS << '@' << getImplementation()->getName(); + switch (getKind()) { + case SILVTable::Entry::Kind::Normal: + break; + case SILVTable::Entry::Kind::Inherited: + OS << " [inherited]"; + break; + case SILVTable::Entry::Kind::Override: + OS << " [override]"; + break; + } + if (isNonOverridden()) { + OS << " [nonoverridden]"; + } + + OS << "\t// " << demangleSymbol(getImplementation()->getName()); +} + void SILVTable::print(llvm::raw_ostream &OS, bool Verbose) const { OS << "sil_vtable "; if (isSerialized()) OS << "[serialized] "; OS << getClass()->getName() << " {\n"; - PrintOptions QualifiedSILTypeOptions = PrintOptions::printQualifiedSILType(); for (auto &entry : getEntries()) { OS << " "; - entry.getMethod().print(OS); - OS << ": "; - - bool HasSingleImplementation = false; - switch (entry.getMethod().kind) { - default: - break; - case SILDeclRef::Kind::IVarDestroyer: - case SILDeclRef::Kind::Destroyer: - case SILDeclRef::Kind::Deallocator: - HasSingleImplementation = true; - } - // No need to emit the signature for methods that may have only - // single implementation, e.g. for destructors. - if (!HasSingleImplementation) { - QualifiedSILTypeOptions.CurrentModule = - entry.getMethod().getDecl()->getDeclContext()->getParentModule(); - entry.getMethod().getDecl()->getInterfaceType().print( - OS, QualifiedSILTypeOptions); - OS << " : "; - } - OS << '@' << entry.getImplementation()->getName(); - switch (entry.getKind()) { - case SILVTable::Entry::Kind::Normal: - break; - case SILVTable::Entry::Kind::Inherited: - OS << " [inherited]"; - break; - case SILVTable::Entry::Kind::Override: - OS << " [override]"; - break; - } - if (entry.isNonOverridden()) { - OS << " [nonoverridden]"; - } - - OS << "\t// " << demangleSymbol(entry.getImplementation()->getName()); + entry.print(OS); OS << "\n"; } OS << "}\n\n"; diff --git a/lib/SIL/IR/SILVTable.cpp b/lib/SIL/IR/SILVTable.cpp index b524586fd4f6b..1bb556736c348 100644 --- a/lib/SIL/IR/SILVTable.cpp +++ b/lib/SIL/IR/SILVTable.cpp @@ -55,6 +55,11 @@ void SILVTable::removeFromVTableCache(Entry &entry) { M.VTableEntryCache.erase({this, entry.getMethod()}); } +void SILVTable::updateVTableCache(const Entry &entry) { + SILModule &M = entry.getImplementation()->getModule(); + M.VTableEntryCache[{this, entry.getMethod()}] = entry; +} + SILVTable::SILVTable(ClassDecl *c, IsSerialized_t serialized, ArrayRef entries) : Class(c), Serialized(serialized), NumEntries(entries.size()) { diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index f9c937de8e63b..aa007ad5c7d66 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -11,9 +11,11 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "libsil" + #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTContext.h" #include "swift/AST/CanTypeVisitor.h" +#include "swift/SIL/SILInstruction.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSIL.h" @@ -392,7 +394,8 @@ namespace { } \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ AbstractionPattern origType) { \ - auto referentType = type->getReferentType(); \ + auto referentType = \ + type->getReferentType()->lookThroughSingleOptionalType(); \ auto concreteType = getConcreteReferenceStorageReferent(referentType); \ if (Name##StorageType::get(concreteType, TC.Context) \ ->isLoadable(Expansion.getResilienceExpansion())) { \ @@ -667,6 +670,26 @@ namespace { return B.createLoad(loc, addr, LoadOwnershipQualifier::Unqualified); } + SILValue emitLoweredLoad(SILBuilder &B, SILLocation loc, SILValue addr, + LoadOwnershipQualifier qual, + TypeExpansionKind) const override { + if (B.getFunction().hasOwnership()) + return B.createLoad(loc, addr, LoadOwnershipQualifier::Trivial); + return B.createLoad(loc, addr, LoadOwnershipQualifier::Unqualified); + } + + void emitLoweredStore(SILBuilder &B, SILLocation loc, SILValue value, + SILValue addr, StoreOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind + expansionKind) const override { + auto storeQual = [&]() -> StoreOwnershipQualifier { + if (B.getFunction().hasOwnership()) + return StoreOwnershipQualifier::Trivial; + return StoreOwnershipQualifier::Unqualified; + }(); + B.createStore(loc, value, addr, storeQual); + } + void emitDestroyAddress(SILBuilder &B, SILLocation loc, SILValue addr) const override { // Trivial @@ -758,8 +781,56 @@ namespace { if (qual != LoadOwnershipQualifier::Copy) return loadValue; + // Otherwise, emit the copy value operation and return our original + // value. This is a small non-ownership optimization to not destabilize + // the optimizer pipeline. + // + // TODO: Once the pass pipeline is fixed, we should evaluate if we can do + // this again. + B.emitCopyValueOperation(loc, loadValue); + return loadValue; + } + + SILValue emitLoweredLoad(SILBuilder &B, SILLocation loc, SILValue addr, + LoadOwnershipQualifier qual, + TypeExpansionKind expansionKind) const override { + if (B.getFunction().hasOwnership()) + return B.createLoad(loc, addr, qual); + + SILValue loadValue = + B.createLoad(loc, addr, LoadOwnershipQualifier::Unqualified); + + // If we do not have a copy, just return the value... + if (qual != LoadOwnershipQualifier::Copy) + return loadValue; + // Otherwise, emit the copy value operation. - return B.emitCopyValueOperation(loc, loadValue); + B.emitLoweredCopyValueOperation(loc, loadValue, expansionKind); + + // Otherwise, emit the copy value operation and return our original + // value. This is a small non-ownership optimization to not destabilize + // the optimizer pipeline. + // + // TODO: Once the pass pipeline is fixed, we should evaluate if we can do + // this again. + return loadValue; + } + + void emitLoweredStore(SILBuilder &B, SILLocation loc, SILValue value, + SILValue addr, StoreOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind + expansionKind) const override { + if (B.getFunction().hasOwnership()) { + B.createStore(loc, value, addr, qual); + return; + } + + if (qual == StoreOwnershipQualifier::Assign) { + SILValue oldValue = B.emitLoadValueOperation( + loc, addr, LoadOwnershipQualifier::Unqualified); + B.emitLoweredDestroyValueOperation(loc, oldValue, expansionKind); + } + B.createStore(loc, value, addr, StoreOwnershipQualifier::Unqualified); } }; @@ -801,6 +872,32 @@ namespace { forExpansion) { } + /// CRTP Default implementation of destructuring an aggregate value. + /// + /// Uses getChildren() and emitRValueProject() to create projections for + /// each child. Subclasses should override this to customize on how + /// destructuring is done. + /// + /// NOTE: Due to the CRTP, this must always be called as + /// asImpl().destructureAggregate() to ensure that one gets the proper + /// implementation! + void destructureAggregate( + SILBuilder &B, SILLocation loc, SILValue aggValue, bool skipTrivial, + function_ref visitor) + const { + for (auto pair : llvm::enumerate(getChildren(B.getModule().Types))) { + auto &child = pair.value(); + auto &childLowering = child.getLowering(); + // Skip trivial children. + if (skipTrivial && childLowering.isTrivial()) + continue; + auto childIndex = child.getIndex(); + auto childValue = asImpl().emitRValueProject(B, loc, aggValue, + childIndex, childLowering); + visitor(pair.index(), childValue, childLowering); + } + } + virtual SILValue rebuildAggregate(SILBuilder &B, SILLocation loc, ArrayRef values) const = 0; @@ -819,16 +916,12 @@ namespace { void forEachNonTrivialChild(SILBuilder &B, SILLocation loc, SILValue aggValue, const T &operation) const { - for (auto &child : getChildren(B.getModule().Types)) { - auto &childLowering = child.getLowering(); - // Skip trivial children. - if (childLowering.isTrivial()) - continue; - auto childIndex = child.getIndex(); - auto childValue = asImpl().emitRValueProject(B, loc, aggValue, - childIndex, childLowering); - operation(B, loc, childIndex, childValue, childLowering); - } + asImpl().destructureAggregate(B, loc, aggValue, true /*skipTrivial*/, + [&](unsigned, SILValue childValue, + const TypeLowering &childLowering) { + operation(B, loc, childValue, + childLowering); + }); } using SimpleOperationTy = void (TypeLowering::*)(SILBuilder &B, @@ -838,10 +931,11 @@ namespace { SILValue aggValue, SimpleOperationTy operation) const { forEachNonTrivialChild(B, loc, aggValue, - [operation](SILBuilder &B, SILLocation loc, IndexType index, - SILValue childValue, const TypeLowering &childLowering) { - (childLowering.*operation)(B, loc, childValue); - }); + [operation](SILBuilder &B, SILLocation loc, + SILValue childValue, + const TypeLowering &childLowering) { + (childLowering.*operation)(B, loc, childValue); + }); } SILValue emitCopyValue(SILBuilder &B, SILLocation loc, @@ -859,21 +953,24 @@ namespace { return emitCopyValue(B, loc, aggValue); } - llvm::SmallVector loweredChildValues; - for (auto &child : getChildren(B.getModule().Types)) { - auto &childLowering = child.getLowering(); - SILValue childValue = asImpl().emitRValueProject(B, loc, aggValue, - child.getIndex(), - childLowering); - if (!childLowering.isTrivial()) { - SILValue loweredChildValue = childLowering.emitLoweredCopyChildValue( - B, loc, childValue, style); - loweredChildValues.push_back(loweredChildValue); - } else { - loweredChildValues.push_back(childValue); - } - } - + SmallVector loweredChildValues; + asImpl().destructureAggregate( + B, loc, aggValue, false /*skipTrivial*/, + [&](unsigned childIndex, SILValue childValue, + const TypeLowering &childLowering) { + if (!childLowering.isTrivial()) + childValue = childLowering.emitLoweredCopyChildValue( + B, loc, childValue, style); + loweredChildValues.push_back(childValue); + }); + + // Without ownership, return our original value. This is a small + // non-ownership optimization to not destabilize the optimizer pipeline. + // + // TODO: Once the pass pipeline is fixed, we should evaluate if we can do + // this again. + if (!B.hasOwnership()) + return aggValue; return rebuildAggregate(B, loc, loweredChildValues); } @@ -910,6 +1007,8 @@ namespace { /// A lowering for loadable but non-trivial tuple types. class LoadableTupleTypeLowering final : public LoadableAggTypeLowering { + using Super = LoadableAggTypeLowering; + public: LoadableTupleTypeLowering(CanType type, RecursiveProperties properties, TypeExpansionContext forExpansion) @@ -918,10 +1017,35 @@ namespace { SILValue emitRValueProject(SILBuilder &B, SILLocation loc, SILValue tupleValue, unsigned index, const TypeLowering &eltLowering) const { + assert(!B.hasOwnership() && + "Shouldn't call this when ownership is enabled?! Destructure " + "non-trivial tuples instead"); return B.createTupleExtract(loc, tupleValue, index, eltLowering.getLoweredType()); } + void destructureAggregate( + SILBuilder &B, SILLocation loc, SILValue aggValue, bool skipTrivial, + function_ref + visitor) const { + // Without ownership, use our parent. + if (!B.hasOwnership()) + return Super::destructureAggregate(B, loc, aggValue, skipTrivial, + visitor); + + // Otherwise, emit a destructure tuple and do the loop. + auto *dti = B.createDestructureTuple(loc, aggValue); + for (auto pair : llvm::enumerate(dti->getResults())) { + SILValue childValue = pair.value(); + auto &childLowering = + B.getFunction().getTypeLowering(childValue->getType()); + if (skipTrivial && childLowering.isTrivial()) + continue; + visitor(pair.index(), childValue, childLowering); + } + } + SILValue rebuildAggregate(SILBuilder &B, SILLocation loc, ArrayRef values) const override { return B.createTuple(loc, getLoweredType(), values); @@ -946,7 +1070,10 @@ namespace { /// A lowering for loadable but non-trivial struct types. class LoadableStructTypeLowering final - : public LoadableAggTypeLowering { + : public LoadableAggTypeLowering { + using Super = + LoadableAggTypeLowering; + public: LoadableStructTypeLowering(CanType type, RecursiveProperties properties, TypeExpansionContext forExpansion) @@ -959,6 +1086,26 @@ namespace { fieldLowering.getLoweredType()); } + void destructureAggregate( + SILBuilder &B, SILLocation loc, SILValue aggValue, bool skipTrivial, + function_ref + visitor) const { + if (!B.hasOwnership()) + return Super::destructureAggregate(B, loc, aggValue, skipTrivial, + visitor); + + auto *dsi = B.createDestructureStruct(loc, aggValue); + for (auto pair : llvm::enumerate(dsi->getResults())) { + SILValue childValue = pair.value(); + auto &childLowering = + B.getFunction().getTypeLowering(childValue->getType()); + if (skipTrivial && childLowering.isTrivial()) + continue; + visitor(pair.index(), childValue, childLowering); + } + } + SILValue rebuildAggregate(SILBuilder &B, SILLocation loc, ArrayRef values) const override { return B.createStruct(loc, getLoweredType(), values); @@ -978,7 +1125,7 @@ namespace { } } }; - + /// A lowering for loadable but non-trivial enum types. class LoadableEnumTypeLowering final : public NonTrivialLoadableTypeLowering { public: @@ -1240,6 +1387,20 @@ namespace { llvm_unreachable("calling emitLoad on non-loadable type"); } + SILValue emitLoweredLoad(SILBuilder &B, SILLocation loc, SILValue addr, + LoadOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind + expansionKind) const override { + llvm_unreachable("calling emitLoweredLoad on non-loadable type?!"); + } + + void emitLoweredStore(SILBuilder &B, SILLocation loc, SILValue value, + SILValue addr, StoreOwnershipQualifier qual, + Lowering::TypeLowering::TypeExpansionKind + expansionKind) const override { + llvm_unreachable("calling emitLoweredStore on non-loadable type?!"); + } + void emitDestroyAddress(SILBuilder &B, SILLocation loc, SILValue addr) const override { if (!isTrivial()) @@ -1451,7 +1612,7 @@ namespace { if (handleResilience(structType, D, properties)) return handleAddressOnly(structType, properties); - if (D->isCxxNotTriviallyCopyable()) { + if (D->isCxxNonTrivial()) { properties.setAddressOnly(); properties.setNonTrivial(); } @@ -2051,6 +2212,17 @@ static CanAnyFunctionType getGlobalAccessorType(CanType varType) { return CanFunctionType::get({}, C.TheRawPointerType); } +/// Removes @noescape from the given type if it's a function type. Otherwise, +/// returns the original type. +static CanType removeNoEscape(CanType resultType) { + if (auto funTy = resultType->getAs()) { + auto newExtInfo = funTy->getExtInfo().withNoEscape(false); + return adjustFunctionType(cast(resultType), newExtInfo); + } + + return resultType; +} + /// Get the type of a default argument generator, () -> T. static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( SILDeclRef c) { @@ -2067,11 +2239,7 @@ static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( // Remove @noescape from function return types. A @noescape // function return type is a contradiction. - if (auto funTy = canResultTy->getAs()) { - auto newExtInfo = funTy->getExtInfo().withNoEscape(false); - canResultTy = - adjustFunctionType(cast(canResultTy), newExtInfo); - } + canResultTy = removeNoEscape(canResultTy); // Get the generic signature from the surrounding context. auto sig = vd->getInnermostDeclContext()->getGenericSignatureOfContext(); @@ -2102,6 +2270,8 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType( if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) { resultTy = originalProperty->getPropertyWrapperInitValueInterfaceType() ->getCanonicalType(); + // Stored property initializers can't return @noescape functions + resultTy = removeNoEscape(resultTy); } } @@ -2140,15 +2310,16 @@ static CanAnyFunctionType getDestructorInterfaceType(DestructorDecl *dd, assert((!isForeign || isDeallocating) && "There are no foreign destroying destructors"); - auto extInfo = - AnyFunctionType::ExtInfo(FunctionType::Representation::Thin, - /*throws*/ false); + auto extInfoBuilder = + AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, + /*throws*/ false); if (isForeign) - extInfo = extInfo - .withSILRepresentation(SILFunctionTypeRepresentation::ObjCMethod); + extInfoBuilder = extInfoBuilder.withSILRepresentation( + SILFunctionTypeRepresentation::ObjCMethod); else - extInfo = extInfo - .withSILRepresentation(SILFunctionTypeRepresentation::Method); + extInfoBuilder = extInfoBuilder.withSILRepresentation( + SILFunctionTypeRepresentation::Method); + auto extInfo = extInfoBuilder.build(); auto &C = dd->getASTContext(); CanType resultTy = (isDeallocating @@ -2174,11 +2345,14 @@ static CanAnyFunctionType getIVarInitDestroyerInterfaceType(ClassDecl *cd, auto resultType = (isDestroyer ? TupleType::getEmpty(cd->getASTContext()) : classType); - auto extInfo = AnyFunctionType::ExtInfo(FunctionType::Representation::Thin, - /*throws*/ false); - extInfo = extInfo - .withSILRepresentation(isObjC? SILFunctionTypeRepresentation::ObjCMethod - : SILFunctionTypeRepresentation::Method); + auto extInfoBuilder = + AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, + /*throws*/ false); + auto extInfo = extInfoBuilder + .withSILRepresentation( + isObjC ? SILFunctionTypeRepresentation::ObjCMethod + : SILFunctionTypeRepresentation::Method) + .build(); resultType = CanFunctionType::get({}, resultType, extInfo); auto sig = cd->getGenericSignature(); @@ -2200,8 +2374,10 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, auto closure = *constant.getAnyFunctionRef(); auto genericSig = getEffectiveGenericSignature(closure, captureInfo); - auto innerExtInfo = AnyFunctionType::ExtInfo(FunctionType::Representation::Thin, - funcType->throws()); + auto innerExtInfo = + AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, + funcType->isThrowing()) + .build(); return CanAnyFunctionType::get( getCanonicalSignatureOrNull(genericSig), diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index be815d3cd89de..47560bd48fba0 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -539,6 +539,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, TSanInoutAccess) CONSTANT_OWNERSHIP_BUILTIN(None, Swift3ImplicitObjCEntrypoint) CONSTANT_OWNERSHIP_BUILTIN(None, PoundAssert) CONSTANT_OWNERSHIP_BUILTIN(None, TypePtrAuthDiscriminator) +CONSTANT_OWNERSHIP_BUILTIN(None, IntInstrprofIncrement) CONSTANT_OWNERSHIP_BUILTIN(None, GlobalStringTablePointer) #undef CONSTANT_OWNERSHIP_BUILTIN diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index b237e70782399..00ceaf52ca02e 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -21,7 +21,6 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Defer.h" -#include "swift/Basic/Timer.h" #include "swift/Demangling/Demangle.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/ParseSILSupport.h" @@ -174,8 +173,8 @@ namespace { /// A callback to be invoked every time a type was deserialized. std::function ParsedTypeCallback; - Type performTypeLocChecking(TypeRepr *TyR, bool IsSILType, - GenericEnvironment *GenericEnv = nullptr); + Type performTypeResolution(TypeRepr *TyR, bool IsSILType, + GenericEnvironment *GenericEnv); void convertRequirements(SILFunction *F, ArrayRef From, SmallVectorImpl &To); @@ -868,9 +867,10 @@ void SILParser::convertRequirements(SILFunction *F, IdentTypeReprLookup PerformLookup(P); // Use parser lexical scopes to resolve references // to the generic parameters. - auto ResolveToInterfaceType = [&](TypeRepr *Ty) -> Type { - Ty->walk(PerformLookup); - return performTypeLocChecking(Ty, /* IsSIL */ false)->mapTypeOutOfContext(); + auto ResolveToInterfaceType = [&](TypeRepr *TyR) -> Type { + TyR->walk(PerformLookup); + return performTypeResolution(TyR, /*IsSILType=*/false, ContextGenericEnv) + ->mapTypeOutOfContext(); }; for (auto &Req : From) { @@ -919,6 +919,7 @@ static bool parseDeclSILOptional(bool *isTransparent, bool *isWeakImported, AvailabilityContext *availability, bool *isWithoutActuallyEscapingThunk, + bool *isAsync, SmallVectorImpl *Semantics, SmallVectorImpl *SpecAttrs, ValueDecl **ClangDecl, @@ -957,6 +958,8 @@ static bool parseDeclSILOptional(bool *isTransparent, else if (isWithoutActuallyEscapingThunk && SP.P.Tok.getText() == "without_actually_escaping") *isWithoutActuallyEscapingThunk = true; + else if (isAsync && SP.P.Tok.getText() == "async") + *isAsync = true; else if (specialPurpose && SP.P.Tok.getText() == "global_init") *specialPurpose = SILFunction::Purpose::GlobalInit; else if (specialPurpose && SP.P.Tok.getText() == "lazy_getter") @@ -1092,16 +1095,14 @@ static bool parseDeclSILOptional(bool *isTransparent, return false; } -Type SILParser::performTypeLocChecking(TypeRepr *T, bool IsSILType, - GenericEnvironment *GenericEnv) { +Type SILParser::performTypeResolution(TypeRepr *TyR, bool IsSILType, + GenericEnvironment *GenericEnv) { if (GenericEnv == nullptr) GenericEnv = ContextGenericEnv; - TypeLoc loc(T); - (void) swift::performTypeLocChecking(P.Context, loc, - /*isSILMode=*/true, IsSILType, - GenericEnv, &P.SF); - return loc.getType(); + return swift::performTypeResolution(TyR, P.Context, + /*isSILMode=*/true, IsSILType, GenericEnv, + &P.SF); } /// Find the top-level ValueDecl or Module given a name. @@ -1155,7 +1156,8 @@ static ValueDecl *lookupMember(Parser &P, Type Ty, DeclBaseName Name, bool SILParser::parseASTType(CanType &result, GenericEnvironment *env) { ParserResult parsedType = P.parseType(); if (parsedType.isNull()) return true; - auto resolvedType = performTypeLocChecking(parsedType.get(), /*IsSILType=*/ false, env); + const auto resolvedType = + performTypeResolution(parsedType.get(), /*isSILType=*/false, env); if (resolvedType->hasError()) return true; @@ -1244,8 +1246,10 @@ bool SILParser::parseSILType(SILType &Result, ParsedGenericEnv = env; // Apply attributes to the type. - auto *attrRepr = P.applyAttributeToType(TyR.get(), attrs, specifier, specifierLoc); - auto Ty = performTypeLocChecking(attrRepr, /*IsSILType=*/true, OuterGenericEnv); + auto *attrRepr = + P.applyAttributeToType(TyR.get(), attrs, specifier, specifierLoc); + const auto Ty = + performTypeResolution(attrRepr, /*IsSILType=*/true, OuterGenericEnv); if (Ty->hasError()) return true; @@ -1697,7 +1701,8 @@ bool SILParser::parseSubstitutions(SmallVectorImpl &parsed, if (defaultForProto) bindProtocolSelfInTypeRepr(TyR.get(), defaultForProto); - auto Ty = performTypeLocChecking(TyR.get(), /*IsSILType=*/ false, GenericEnv); + const auto Ty = + performTypeResolution(TyR.get(), /*IsSILType=*/false, GenericEnv); if (Ty->hasError()) return true; parsed.push_back({Loc, Ty}); @@ -1781,7 +1786,7 @@ SubstitutionMap getApplySubstitutionsFromParsed( return conformance; SP.P.diagnose(loc, diag::sil_substitution_mismatch, replacementType, - proto->getDeclaredType()); + proto->getDeclaredInterfaceType()); failed = true; return ProtocolConformanceRef(proto); @@ -2106,7 +2111,8 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Member, bool FnTypeRequired) { } } - auto Ty = performTypeLocChecking(TyR.get(), /*IsSILType=*/ false, genericEnv); + const auto Ty = + performTypeResolution(TyR.get(), /*IsSILType=*/false, genericEnv); if (Ty->hasError()) return true; @@ -2613,8 +2619,6 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, StringLiteralInst::Encoding encoding; if (P.Tok.getText() == "utf8") { encoding = StringLiteralInst::Encoding::UTF8; - } else if (P.Tok.getText() == "utf16") { - encoding = StringLiteralInst::Encoding::UTF16; } else if (P.Tok.getText() == "objc_selector") { encoding = StringLiteralInst::Encoding::ObjCSelector; } else if (P.Tok.getText() == "bytes") { @@ -5678,6 +5682,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { bool isWeakImported = false; AvailabilityContext availability = AvailabilityContext::alwaysAvailable(); bool isWithoutActuallyEscapingThunk = false; + bool isAsync = false; Inline_t inlineStrategy = InlineDefault; OptimizationMode optimizationMode = OptimizationMode::NotSet; SmallVector Semantics; @@ -5692,8 +5697,8 @@ bool SILParserState::parseDeclSIL(Parser &P) { &isThunk, &isDynamic, &isExactSelfClass, &DynamicallyReplacedFunction, &objCReplacementFor, &specialPurpose, &inlineStrategy, &optimizationMode, nullptr, &isWeakImported, &availability, - &isWithoutActuallyEscapingThunk, &Semantics, - &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || + &isWithoutActuallyEscapingThunk, &isAsync, &Semantics, &SpecAttrs, + &ClangDecl, &MRK, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || P.parseIdentifier(FnName, FnNameLoc, diag::expected_sil_function_name) || P.parseToken(tok::colon, diag::expected_sil_type)) @@ -5731,6 +5736,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { FunctionState.F->setAvailabilityForLinkage(availability); FunctionState.F->setWithoutActuallyEscapingThunk( isWithoutActuallyEscapingThunk); + FunctionState.F->setAsync(isAsync); FunctionState.F->setInlineStrategy(inlineStrategy); FunctionState.F->setOptimizationMode(optimizationMode); FunctionState.F->setEffectsKind(MRK); @@ -5916,10 +5922,9 @@ bool SILParserState::parseSILGlobal(Parser &P) { SILParser State(P); if (parseSILLinkage(GlobalLinkage, P) || parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, - &isLet, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, State, M) || + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, &isLet, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, State, M) || P.parseToken(tok::at_sign, diag::expected_sil_value_name) || P.parseIdentifier(GlobalName, NameLoc, diag::expected_sil_value_name) || P.parseToken(tok::colon, diag::expected_sil_type)) @@ -5968,7 +5973,7 @@ bool SILParserState::parseSILProperty(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, SP, M)) + nullptr, nullptr, nullptr, nullptr, SP, M)) return true; ValueDecl *VD; @@ -6038,8 +6043,7 @@ bool SILParserState::parseSILVTable(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, - VTableState, M)) + nullptr, nullptr, nullptr, nullptr, VTableState, M)) return true; // Parse the class name. @@ -6322,7 +6326,8 @@ ProtocolConformanceRef SILParser::parseProtocolConformanceHelper( bindProtocolSelfInTypeRepr(TyR.get(), defaultForProto); } - auto ConformingTy = performTypeLocChecking(TyR.get(), /*IsSILType=*/ false, witnessEnv); + const auto ConformingTy = + performTypeResolution(TyR.get(), /*IsSILType=*/false, witnessEnv); if (ConformingTy->hasError()) return ProtocolConformanceRef(); @@ -6438,17 +6443,18 @@ static bool parseSILVTableEntry( ParserResult TyR = P.parseType(); if (TyR.isNull()) return true; - TypeLoc Ty = TyR.get(); + if (isDefaultWitnessTable) bindProtocolSelfInTypeRepr(TyR.get(), proto); - if (swift::performTypeLocChecking(P.Context, Ty, - /*isSILMode=*/false, - /*isSILType=*/false, - witnessEnv, - &P.SF)) + + const auto Ty = + swift::performTypeResolution(TyR.get(), P.Context, + /*isSILMode=*/false, + /*isSILType=*/false, witnessEnv, &P.SF); + if (Ty->hasError()) return true; - assocOrSubject = Ty.getType()->getCanonicalType(); + assocOrSubject = Ty->getCanonicalType(); } if (!assocOrSubject) return true; @@ -6499,19 +6505,19 @@ static bool parseSILVTableEntry( ParserResult TyR = P.parseType(); if (TyR.isNull()) return true; - TypeLoc Ty = TyR.get(); + if (isDefaultWitnessTable) bindProtocolSelfInTypeRepr(TyR.get(), proto); - if (swift::performTypeLocChecking(P.Context, Ty, - /*isSILMode=*/false, - /*isSILType=*/false, - witnessEnv, - &P.SF)) + + const auto Ty = + swift::performTypeResolution(TyR.get(), P.Context, + /*isSILMode=*/false, + /*isSILType=*/false, witnessEnv, &P.SF); + if (Ty->hasError()) return true; - witnessEntries.push_back(SILWitnessTable::AssociatedTypeWitness{ - assoc, Ty.getType()->getCanonicalType() - }); + witnessEntries.push_back( + SILWitnessTable::AssociatedTypeWitness{assoc, Ty->getCanonicalType()}); return false; } @@ -6573,8 +6579,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) { if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, - WitnessState, M)) + nullptr, nullptr, nullptr, nullptr, WitnessState, M)) return true; Scope S(&P, ScopeKind::TopLevel); diff --git a/lib/SIL/Utils/DynamicCasts.cpp b/lib/SIL/Utils/DynamicCasts.cpp index fcdbd25fd0268..ffbd852e73ab5 100644 --- a/lib/SIL/Utils/DynamicCasts.cpp +++ b/lib/SIL/Utils/DynamicCasts.cpp @@ -205,7 +205,7 @@ static CanType getHashableExistentialType(ModuleDecl *M) { auto hashable = M->getASTContext().getProtocol(KnownProtocolKind::Hashable); if (!hashable) return CanType(); - return hashable->getDeclaredType()->getCanonicalType(); + return hashable->getDeclaredInterfaceType()->getCanonicalType(); } /// Check if a given type conforms to _BridgedToObjectiveC protocol. @@ -492,9 +492,14 @@ swift::classifyDynamicCast(ModuleDecl *M, // A function cast can succeed if the function types can be identical, // or if the target type is throwier than the original. + // An async function cannot be cast to a non-async function and + // vice-versa. + if (sourceFunction->isAsync() != targetFunction->isAsync()) + return DynamicCastFeasibility::WillFail; + // A non-throwing source function can be cast to a throwing target type, // but not vice versa. - if (sourceFunction->throws() && !targetFunction->throws()) + if (sourceFunction->isThrowing() && !targetFunction->isThrowing()) return DynamicCastFeasibility::WillFail; // The cast can't change the representation at runtime. diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 95f311909f7c2..73e84417f3fbb 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -327,6 +327,21 @@ bool swift::isSanitizerInstrumentation(SILInstruction *Instruction) { return false; } +// Instrumentation instructions should not affect the correctness of the +// program. That is, they should not affect the observable program state. +// The constant evaluator relies on this property to skip instructions. +bool swift::isInstrumentation(SILInstruction *Instruction) { + if (isSanitizerInstrumentation(Instruction)) + return true; + + if (BuiltinInst *bi = dyn_cast(Instruction)) { + if (bi->getBuiltinKind() == BuiltinValueKind::IntInstrprofIncrement) + return true; + } + + return false; +} + SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { // A partial_apply of a reabstraction thunk either has a single capture // (a function) or two captures (function and dynamic Self type). diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 2841239bf0e0c..dedba04a24ce1 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -25,40 +25,17 @@ using namespace swift; // MARK: General Helpers //===----------------------------------------------------------------------===// -SILValue swift::stripAccessMarkers(SILValue v) { - while (auto *bai = dyn_cast(v)) { - v = bai->getOperand(); - } - return v; -} - -// The resulting projection must have an address-type operand at index zero -// representing the projected address. -SingleValueInstruction *swift::isAccessProjection(SILValue v) { - switch (v->getKind()) { - default: - return nullptr; - - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::TailAddrInst: - case ValueKind::IndexAddrInst: - return cast(v); - }; -} - // TODO: When the optimizer stops stripping begin_access markers, then we should // be able to assert that the result is a BeginAccessInst and the default case // is unreachable. SILValue swift::getAddressAccess(SILValue v) { while (true) { assert(v->getType().isAddress()); - auto *projection = isAccessProjection(v); + auto projection = AccessProjection(v); if (!projection) return v; - v = projection->getOperand(0); + v = projection.baseAddress(); } } @@ -142,6 +119,12 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { auto *REA = cast(base); value = stripBorrow(REA->getOperand()); setElementIndex(REA->getFieldNo()); + break; + } + case Tail: { + auto *RTA = cast(base); + value = stripBorrow(RTA->getOperand()); + break; } } } @@ -175,6 +158,9 @@ const ValueDecl *AccessedStorage::getDecl() const { auto *decl = getObject()->getType().getNominalOrBoundGenericNominal(); return decl->getStoredProperties()[getPropertyIndex()]; } + case Tail: + return nullptr; + case Argument: return getArgument()->getDecl(); @@ -208,11 +194,17 @@ const char *AccessedStorage::getKindName(AccessedStorage::Kind k) { return "Global"; case Class: return "Class"; + case Tail: + return "Tail"; } llvm_unreachable("unhandled kind"); } void AccessedStorage::print(raw_ostream &os) const { + if (!*this) { + os << "INVALID\n"; + return; + } os << getKindName(getKind()) << " "; switch (getKind()) { case Box: @@ -234,32 +226,132 @@ void AccessedStorage::print(raw_ostream &os) const { getDecl()->print(os); os << " Index: " << getPropertyIndex() << "\n"; break; + case Tail: + os << getObject(); + os << " Tail\n"; } } void AccessedStorage::dump() const { print(llvm::dbgs()); } namespace { -struct FindAccessedStorageVisitor - : public AccessUseDefChainVisitor -{ - SmallVector addressWorklist; - SmallPtrSet visitedPhis; +// Find common AccessedStorage that leads to all arguments of a given +// pointer phi use. Return an invalid SILValue on failure. +// +// Also guarantees that all phi inputs follow the same access path. If any phi +// inputs have different access path components, then the phi is considered an +// invalid access. This is ok because path components always have an address +// type, and we are phasing out all address-type phis. Pointer-phis will +// continue to be allowed but they cannot affect the access path. +template +class FindPhiStorageVisitor + : public AccessUseDefChainVisitor> { + StorageVisitor &storageVisitor; + Optional commonDefinition; + SmallVector pointerWorklist; + SmallPtrSet nestedPhis; + +public: + FindPhiStorageVisitor(StorageVisitor &storageVisitor) + : storageVisitor(storageVisitor) {} + + // Main entry point. + void findPhiStorage(SILPhiArgument *phiArg) { + // Visiting a phi will call storageVisitor to set the storage result + // whenever it finds a base. + visitPhi(phiArg); + while (!pointerWorklist.empty()) { + this->visit(pointerWorklist.pop_back_val()); + } + // If a common path component was found, recursively look for the storage. + if (commonDefinition) { + if (commonDefinition.getValue()) { + auto storage = storageVisitor.findStorage(commonDefinition.getValue()); + (void)storage; // The same storageVisitor called us. It has already + // recorded the storage that it found. + } else { + // If divergent paths were found, invalidate any previously discovered + // storage. + storageVisitor.setStorage(AccessedStorage()); + } + } + } + + // Visitor helper. + void setDefinition(SILValue def) { + if (!commonDefinition) { + commonDefinition = def; + return; + } + if (commonDefinition.getValue() != def) + commonDefinition = SILValue(); + } + + // MARK: Visitor implementation. + + void checkResult(SILValue result) { + assert(!result && "must override any visitor that returns a result"); + } + + // Recursively call the original storageVisitor for each base. We can't simply + // look for a common definition on all phi inputs, because the base may be + // cloned on each path. For example, two global_addr instructions may refer to + // the same global storage. Those global_addr instructions may each be + // converted to a RawPointer before being passed into the non-address phi. + void visitBase(SILValue base, AccessedStorage::Kind kind) { + checkResult(storageVisitor.visitBase(base, kind)); + } + + void visitNonAccess(SILValue value) { + checkResult(storageVisitor.visitNonAccess(value)); + } + + void visitNestedAccess(BeginAccessInst *access) { + checkResult(storageVisitor.visitNestedAccess(access)); + } + + void visitPhi(SILPhiArgument *phiArg) { + if (nestedPhis.insert(phiArg).second) + phiArg->getIncomingPhiValues(pointerWorklist); + } + + void visitCast(SingleValueInstruction *projectedAddr, Operand *parentAddr) { + // Allow conversions to/from pointers and addresses on disjoint phi paths. + this->pointerWorklist.push_back(parentAddr->get()); + } + + void visitPathComponent(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + // Path components are not expected to occur on disjoint phi paths. Stop + // searching at this projection. + setDefinition(projectedAddr); + } +}; +} // namespace + +namespace { +// Implementation of AccessUseDefChainVisitor that looks for a single common +// AccessedStorage object for all projection paths. +template +class FindAccessedStorageVisitorBase + : public AccessUseDefChainVisitor { +protected: Optional storage; + SmallPtrSet visitedPhis; public: - FindAccessedStorageVisitor(SILValue firstSourceAddr) - : addressWorklist({firstSourceAddr}) - {} - - AccessedStorage doIt() { - while (!addressWorklist.empty()) { - visit(addressWorklist.pop_back_val()); + // Main entry point. May be called reentrantly by the phi visitor. + AccessedStorage findStorage(SILValue sourceAddr) { + SILValue nextAddr = this->visit(sourceAddr); + while (nextAddr) { + assert(nextAddr->getType().isAddress() + || isa(nextAddr->getType().getASTType()) + || isa(nextAddr->getType().getASTType())); + nextAddr = this->visit(nextAddr); } - return storage.getValueOr(AccessedStorage()); } - + void setStorage(AccessedStorage foundStorage) { if (!storage) { storage = foundStorage; @@ -267,45 +359,65 @@ struct FindAccessedStorageVisitor // `storage` may still be invalid. If both `storage` and `foundStorage` // are invalid, this check passes, but we return an invalid storage // below. - if (!storage->hasIdenticalBase(foundStorage)) { + if (!storage->hasIdenticalBase(foundStorage)) storage = AccessedStorage(); - addressWorklist.clear(); - } } } - - void visitBase(SILValue base, AccessedStorage::Kind kind) { + + // MARK: visitor implementation. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { setStorage(AccessedStorage(base, kind)); + return SILValue(); } - - void visitNonAccess(SILValue value) { + + SILValue visitNonAccess(SILValue value) { setStorage(AccessedStorage()); + return SILValue(); } - - void visitPhi(SILPhiArgument *phiArg) { + + SILValue visitPhi(SILPhiArgument *phiArg) { + // Cycles involving phis are only handled within FindPhiStorageVisitor. + // Path components are not allowed in phi cycles. if (visitedPhis.insert(phiArg).second) { - phiArg->getIncomingPhiValues(addressWorklist); + FindPhiStorageVisitor(this->asImpl()).findPhiStorage(phiArg); + return SILValue(); } + // Cannot treat unresolved phis as "unidentified" because they may alias + // with global or class access. + return visitNonAccess(phiArg); } - - void visitIncomplete(SILValue projectedAddr, SILValue parentAddr) { - addressWorklist.push_back(parentAddr); + + SILValue visitCast(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + return parentAddr->get(); + } + + SILValue visitPathComponent(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + return parentAddr->get(); + } +}; + +struct FindAccessedStorageVisitor + : public FindAccessedStorageVisitorBase { + + SILValue visitNestedAccess(BeginAccessInst *access) { + return access->getSource(); } }; + +struct IdentifyAccessedStorageVisitor + : public FindAccessedStorageVisitorBase {}; + } // namespace AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { - return FindAccessedStorageVisitor(sourceAddr).doIt(); + return FindAccessedStorageVisitor().findStorage(sourceAddr); } -AccessedStorage swift::findAccessedStorageNonNested(SILValue sourceAddr) { - while (true) { - const AccessedStorage &storage = findAccessedStorage(sourceAddr); - if (!storage || storage.getKind() != AccessedStorage::Nested) - return storage; - - sourceAddr = cast(storage.getValue())->getSource(); - } +AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) { + return IdentifyAccessedStorageVisitor().findStorage(sourceAddr); } //===----------------------------------------------------------------------===// @@ -397,7 +509,7 @@ bool swift::isSingleInitAllocStack(AllocStackInst *asi, continue; } - // Otherwise, if we have found something not in our whitelist, return false. + // Otherwise, if we have found something not in our allowlist, return false. return false; } @@ -467,6 +579,10 @@ void swift::checkSwitchEnumBlockArg(SILPhiArgument *arg) { bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F) { switch (storage.getKind()) { + case AccessedStorage::Nested: + assert(false && "don't pass nested storage to this helper"); + return false; + case AccessedStorage::Box: case AccessedStorage::Stack: if (isScratchBuffer(storage.getValue())) @@ -476,24 +592,16 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, break; case AccessedStorage::Class: break; + case AccessedStorage::Tail: + return false; + case AccessedStorage::Yield: // Yields are accessed by the caller. return false; case AccessedStorage::Argument: // Function arguments are accessed by the caller. return false; - case AccessedStorage::Nested: { - // A begin_access is considered a separate base for the purpose of conflict - // checking. However, for the purpose of inserting unenforced markers and - // performaing verification, it needs to be ignored. - auto *BAI = cast(storage.getValue()); - const AccessedStorage &nestedStorage = - findAccessedStorage(BAI->getSource()); - if (!nestedStorage) - return false; - return isPossibleFormalAccessBase(nestedStorage, F); - } case AccessedStorage::Unidentified: if (isAddressForLocalInitOnly(storage.getValue())) return false; @@ -512,6 +620,8 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, if (isScratchBuffer(storage.getValue())) return false; + + break; } // Additional checks that apply to anything that may fall through. diff --git a/lib/SIL/Utils/OptimizationRemark.cpp b/lib/SIL/Utils/OptimizationRemark.cpp index 25f90445e42f0..00da367598e31 100644 --- a/lib/SIL/Utils/OptimizationRemark.cpp +++ b/lib/SIL/Utils/OptimizationRemark.cpp @@ -19,31 +19,41 @@ #include "swift/SIL/OptimizationRemark.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/Demangling/Demangler.h" +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILRemarkStreamer.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" using namespace swift; using namespace OptRemark; -Argument::Argument(StringRef key, int n) : key(key), val(llvm::itostr(n)) {} +Argument::Argument(StringRef key, int n) + : key(ArgumentKeyKind::Default, key), val(llvm::itostr(n)) {} -Argument::Argument(StringRef key, long n) : key(key), val(llvm::itostr(n)) {} +Argument::Argument(StringRef key, long n) + : key(ArgumentKeyKind::Default, key), val(llvm::itostr(n)) {} Argument::Argument(StringRef key, long long n) - : key(key), val(llvm::itostr(n)) {} + : key(ArgumentKeyKind::Default, key), val(llvm::itostr(n)) {} Argument::Argument(StringRef key, unsigned n) - : key(key), val(llvm::utostr(n)) {} + : key(ArgumentKeyKind::Default, key), val(llvm::utostr(n)) {} Argument::Argument(StringRef key, unsigned long n) - : key(key), val(llvm::utostr(n)) {} + : key(ArgumentKeyKind::Default, key), val(llvm::utostr(n)) {} Argument::Argument(StringRef key, unsigned long long n) - : key(key), val(llvm::utostr(n)) {} + : key(ArgumentKeyKind::Default, key), val(llvm::utostr(n)) {} -Argument::Argument(StringRef key, SILFunction *f) : key(key) { +Argument::Argument(ArgumentKey key, SILFunction *f) : key(key) { auto options = Demangle::DemangleOptions::SimplifiedUIDemangleOptions(); // Enable module names so that we have a way of filtering out // stdlib-related remarks. @@ -57,25 +67,37 @@ Argument::Argument(StringRef key, SILFunction *f) : key(key) { loc = f->getLocation().getSourceLoc(); } -Argument::Argument(StringRef key, SILType ty) : key(key) { +Argument::Argument(StringRef key, SILType ty) + : key(ArgumentKeyKind::Default, key) { llvm::raw_string_ostream stream(val); - ty.print(stream); + PrintOptions subPrinter = PrintOptions::printSIL(); + ty.getASTType().print(stream, subPrinter); } -Argument::Argument(StringRef key, CanType ty) : key(key) { +Argument::Argument(StringRef key, CanType ty) + : key(ArgumentKeyKind::Default, key) { llvm::raw_string_ostream stream(val); ty.print(stream); } -template std::string Remark::getMsg() const { +template +std::string Remark::getMsg() const { std::string str; llvm::raw_string_ostream stream(str); - for (const Argument &arg : args) + // Go through our args and if we are not emitting for diagnostics *OR* we are + // emitting for diagnostics and this argument is not intended to be emitted as + // a diagnostic separate from our main remark, emit the arg value here. + for (const Argument &arg : args) { + if (arg.key.kind.isSeparateDiagnostic()) + continue; stream << arg.val; + } + return stream.str(); } -template std::string Remark::getDebugMsg() const { +template +std::string Remark::getDebugMsg() const { std::string str; llvm::raw_string_ostream stream(str); @@ -89,37 +111,166 @@ template std::string Remark::getDebugMsg() const { return stream.str(); } -Emitter::Emitter(StringRef passName, SILModule &m) - : module(m), passName(passName), +static bool hasForceEmitSemanticAttr(SILFunction &fn, StringRef passName) { + return llvm::any_of(fn.getSemanticsAttrs(), [&](const std::string &str) { + auto ref = StringRef(str); + + // First try to chomp the prefix. + if (!ref.consume_front(semantics::FORCE_EMIT_OPT_REMARK_PREFIX)) + return false; + + // Then see if we only have the prefix. Then always return true the user + // wants /all/ remarks. + if (ref.empty()) + return true; + + // Otherwise, lets try to chomp the '.' and then the passName. + if (!ref.consume_front(".") || !ref.consume_front(passName)) + return false; + + return ref.empty(); + }); +} + +Emitter::Emitter(StringRef passName, SILFunction &fn) + : fn(fn), passName(passName), passedEnabled( - m.getASTContext().LangOpts.OptimizationRemarkPassedPattern && - m.getASTContext().LangOpts.OptimizationRemarkPassedPattern->match( - passName)), + hasForceEmitSemanticAttr(fn, passName) || + (fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern && + fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern->match( + passName))), missedEnabled( - m.getASTContext().LangOpts.OptimizationRemarkMissedPattern && - m.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match( - passName)) {} + hasForceEmitSemanticAttr(fn, passName) || + (fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern && + fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match( + passName))) {} + +/// The user has passed us an instruction that for some reason has a source loc +/// that can not be used. Search down the current block for an instruction with +/// a valid source loc and use that instead. +static SourceLoc inferOptRemarkSearchForwards(SILInstruction &i) { + for (auto &inst : + llvm::make_range(std::next(i.getIterator()), i.getParent()->end())) { + auto newLoc = inst.getLoc().getSourceLoc(); + if (newLoc.isValid()) + return newLoc; + } + + return SourceLoc(); +} + +/// The user has passed us an instruction that for some reason has a source loc +/// that can not be used. Search up the current block for an instruction with +/// a valid SILLocation and use the end SourceLoc of the SourceRange for the +/// instruction. +static SourceLoc inferOptRemarkSearchBackwards(SILInstruction &i) { + for (auto &inst : llvm::make_range(std::next(i.getReverseIterator()), + i.getParent()->rend())) { + auto loc = inst.getLoc(); + if (!bool(loc)) + continue; + + auto range = loc.getSourceRange(); + if (range.isValid()) + return range.End; + } + + return SourceLoc(); +} + +SourceLoc swift::OptRemark::inferOptRemarkSourceLoc( + SILInstruction &i, SourceLocInferenceBehavior inferBehavior) { + auto loc = i.getLoc().getSourceLoc(); + + // Do a quick check if we already have a valid loc. In such a case, just + // return. Otherwise, we try to infer using one of our heuristics below. + if (loc.isValid()) + return loc; + + // Otherwise, try to handle the individual behavior cases, returning loc at + // the end of each case (its invalid, so it will get ignored). If loc is not + // returned, we hit an assert at the end to make it easy to identify a case + // was missed. + switch (inferBehavior) { + case SourceLocInferenceBehavior::None: + return loc; + case SourceLocInferenceBehavior::ForwardScanOnly: { + SourceLoc newLoc = inferOptRemarkSearchForwards(i); + if (newLoc.isValid()) + return newLoc; + return loc; + } + case SourceLocInferenceBehavior::BackwardScanOnly: { + SourceLoc newLoc = inferOptRemarkSearchBackwards(i); + if (newLoc.isValid()) + return newLoc; + return loc; + } + case SourceLocInferenceBehavior::ForwardThenBackward: { + SourceLoc newLoc = inferOptRemarkSearchForwards(i); + if (newLoc.isValid()) + return newLoc; + newLoc = inferOptRemarkSearchBackwards(i); + if (newLoc.isValid()) + return newLoc; + return loc; + } + case SourceLocInferenceBehavior::BackwardThenForward: { + SourceLoc newLoc = inferOptRemarkSearchBackwards(i); + if (newLoc.isValid()) + return newLoc; + newLoc = inferOptRemarkSearchForwards(i); + if (newLoc.isValid()) + return newLoc; + return loc; + } + } + + llvm_unreachable("Covered switch isn't covered?!"); +} template -static void emitRemark(SILModule &module, const Remark &remark, +static void emitRemark(SILFunction &fn, const Remark &remark, Diag id, bool diagEnabled) { if (remark.getLocation().isInvalid()) return; + + auto &module = fn.getModule(); if (auto *remarkStreamer = module.getSILRemarkStreamer()) remarkStreamer->emit(remark); - if (diagEnabled) - module.getASTContext().Diags.diagnose(remark.getLocation(), id, - remark.getMsg()); + + // If diagnostics are enabled, first emit the main diagnostic and then loop + // through our arguments and allow the arguments to add additional diagnostics + // if they want. + if (!diagEnabled && !fn.hasSemanticsAttrThatStartsWith( + semantics::FORCE_EMIT_OPT_REMARK_PREFIX)) + return; + + auto &de = module.getASTContext().Diags; + de.diagnoseWithNotes( + de.diagnose(remark.getLocation(), id, remark.getMsg()), [&]() { + for (auto &arg : remark.getArgs()) { + switch (arg.key.kind) { + case ArgumentKeyKind::Default: + continue; + case ArgumentKeyKind::Note: + de.diagnose(arg.loc, diag::opt_remark_note, arg.val); + continue; + case ArgumentKeyKind::ParentLocNote: + de.diagnose(remark.getLocation(), diag::opt_remark_note, arg.val); + continue; + } + llvm_unreachable("Unhandled case?!"); + } + }); } void Emitter::emit(const RemarkPassed &remark) { - emitRemark(module, remark, diag::opt_remark_passed, - isEnabled()); + emitRemark(fn, remark, diag::opt_remark_passed, isEnabled()); } void Emitter::emit(const RemarkMissed &remark) { - emitRemark(module, remark, diag::opt_remark_missed, - isEnabled()); + emitRemark(fn, remark, diag::opt_remark_missed, isEnabled()); } void Emitter::emitDebug(const RemarkPassed &remark) { diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index 1b414791e15c0..1f5e84803d5b9 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -486,6 +486,11 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( // validate that all uses of the project_box are not writes that overlap with // our load_borrow's result. These are things that may not be a formal access // base. + // + // FIXME: we don't seem to verify anywhere that a pointer_to_address cannot + // itself be derived from another address that is accessible in the same + // function, either because it was returned from a call or directly + // address_to_pointer'd. if (isa(address)) { return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, address); @@ -525,6 +530,9 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( } case AccessedStorage::Yield: { // For now, do this. Otherwise, check for in_guaranteed, etc. + // + // FIXME: The yielded address could overlap with another address in this + // function. return true; } case AccessedStorage::Box: { @@ -534,9 +542,18 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( case AccessedStorage::Class: { // Check that the write to the class's memory doesn't overlap with our // load_borrow. + // + // FIXME: how do we know that other projections of the same object don't + // occur within the same function? return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, address); } + case AccessedStorage::Tail: { + // This should be as strong as the Class address case, but to handle it we + // need to find all aliases of the object and all tail projections within + // that object. + return false; + } case AccessedStorage::Global: { // Check that the write to the class's memory doesn't overlap with our // load_borrow. diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 5690e4a93cf38..83912db313ffb 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -909,7 +909,7 @@ class SILVerifier : public SILVerifierBase { return InstNumbers[a] < InstNumbers[b]; } - // FIXME: For sanity, address-type block args should be prohibited at all SIL + // FIXME: For sanity, address-type phis should be prohibited at all SIL // stages. However, the optimizer currently breaks the invariant in three // places: // 1. Normal Simplify CFG during conditional branch simplification @@ -917,17 +917,14 @@ class SILVerifier : public SILVerifierBase { // 2. Simplify CFG via Jump Threading. // 3. Loop Rotation. // + // BasicBlockCloner::canCloneInstruction and sinkAddressProjections is + // designed to avoid this issue, we just need to make sure all passes use it + // correctly. // - bool prohibitAddressBlockArgs() { - // If this function was deserialized from canonical SIL, this invariant may - // already have been violated regardless of this module's SIL stage or - // exclusivity enforcement level. Presumably, access markers were already - // removed prior to serialization. - if (F.wasDeserializedCanonical()) - return false; - - SILModule &M = F.getModule(); - return M.getStage() == SILStage::Raw; + // Minimally, we must prevent address-type phis as long as access markers are + // preserved. A goal is to preserve access markers in OSSA. + bool prohibitAddressPhis() { + return F.hasOwnership(); } void visitSILPhiArgument(SILPhiArgument *arg) { @@ -943,9 +940,9 @@ class SILVerifier : public SILVerifierBase { } } else { } - if (arg->isPhiArgument() && prohibitAddressBlockArgs()) { - // As a property of well-formed SIL, we disallow address-type block - // arguments. Supporting them would prevent reliably reasoning about the + if (arg->isPhiArgument() && prohibitAddressPhis()) { + // As a property of well-formed SIL, we disallow address-type + // phis. Supporting them would prevent reliably reasoning about the // underlying storage of memory access. This reasoning is important for // diagnosing violations of memory access rules and supporting future // optimizations such as bitfield packing. Address-type block arguments @@ -1939,29 +1936,29 @@ class SILVerifier : public SILVerifierBase { } // Verify that all formal accesses patterns are recognized as part of a - // whitelist. The presence of an unknown pattern means that analysis will + // allowlist. The presence of an unknown pattern means that analysis will // silently fail, and the compiler may be introducing undefined behavior // with no other way to detect it. // // For example, AccessEnforcementWMO runs very late in the // pipeline and assumes valid storage for all dynamic Read/Modify access. It - // also requires that Unidentified access fit a whitelist on known + // also requires that Unidentified access fit a allowlist on known // non-internal globals or class properties. // - // First check that findAccessedStorage returns without asserting. For + // First check that identifyFormalAccess returns without asserting. For // Unsafe enforcement, that is sufficient. For any other enforcement // level also require that it returns a valid AccessedStorage object. // Unsafe enforcement is used for some unrecognizable access patterns, // like debugger variables. The compiler never cares about the source of // those accesses. - findAccessedStorage(BAI->getSource()); + identifyFormalAccess(BAI); // FIXME: rdar://57291811 - the following check for valid storage will be // reenabled shortly. A fix is planned. In the meantime, the possiblity that // a real miscompilation could be caused by this failure is insignificant. // I will probably enable a much broader SILVerification of address-type // block arguments first to ensure we never hit this check again. /* - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + AccessedStorage storage = identifyFormalAccess(BAI); if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe) require(storage, "Unknown formal access pattern"); */ @@ -2000,8 +1997,8 @@ class SILVerifier : public SILVerifierBase { break; } - // First check that findAccessedStorage never asserts. - AccessedStorage storage = findAccessedStorage(BUAI->getSource()); + // First check that identifyFormalAccess never asserts. + AccessedStorage storage = identifyFormalAccess(BUAI); // Only allow Unsafe and Builtin access to have invalid storage. if (BUAI->getEnforcement() != SILAccessEnforcement::Unsafe && !BUAI->isFromBuiltin()) { @@ -3717,10 +3714,14 @@ class SILVerifier : public SILVerifierBase { auto adjustedOperandExtInfo = opFTy->getExtInfo() + .intoBuilder() .withRepresentation(SILFunctionType::Representation::Thick) - .withNoEscape(resFTy->isNoEscape()); - require(adjustedOperandExtInfo == resFTy->getExtInfo(), - "operand and result of thin_to_think_function must agree in particulars"); + .withNoEscape(resFTy->isNoEscape()) + .build(); + require(adjustedOperandExtInfo.isEqualTo(resFTy->getExtInfo(), + useClangTypes(opFTy)), + "operand and result of thin_to_think_function must agree in " + "particulars"); } void checkThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCI) { @@ -5384,6 +5385,10 @@ void SILVTable::verify(const SILModule &M) const { for (unsigned i : indices(getEntries())) { auto &entry = getEntries()[i]; + // Make sure the module's lookup cache is consistent. + assert(entry == *getEntry(const_cast(M), entry.getMethod()) + && "vtable entry is out of sync with method's vtable cache"); + // All vtable entries must be decls in a class context. assert(entry.getMethod().hasDecl() && "vtable entry is not a decl"); auto baseInfo = M.Types.getConstantInfo(TypeExpansionContext::minimal(), diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index bc93cdf2387a3..cd36b343f0e58 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -29,7 +29,6 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" -#include "swift/Basic/Timer.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" @@ -51,8 +50,7 @@ using namespace Lowering; SILGenModule::SILGenModule(SILModule &M, ModuleDecl *SM) : M(M), Types(M.Types), SwiftModule(SM), TopLevelSGF(nullptr), - MagicFileStringsByFilePath( - SM->computeMagicFileStringMap(/*shouldDiagnose=*/true)) { + FileIDsByFilePath(SM->computeFileIDMap(/*shouldDiagnose=*/true)) { const SILOptions &Opts = M.getOptions(); if (!Opts.UseProfile.empty()) { auto ReaderOrErr = llvm::IndexedInstrProfReader::create(Opts.UseProfile); @@ -413,12 +411,13 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, : ParameterConvention::Indirect_In_Guaranteed }, }; - auto extInfo = SILFunctionType::ExtInfo( - SILFunctionTypeRepresentation::Thin, - /*pseudogeneric*/ false, - /*non-escaping*/ false, - DifferentiabilityKind::NonDifferentiable, - /*clangFunctionType*/ nullptr); + auto extInfo = + SILFunctionType::ExtInfoBuilder(SILFunctionTypeRepresentation::Thin, + /*pseudogeneric*/ false, + /*non-escaping*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr) + .build(); auto functionTy = SILFunctionType::get(sig, extInfo, SILCoroutineKind::YieldOnce, @@ -730,7 +729,8 @@ static void emitDelayedFunction(SILGenModule &SGM, switch (param->getDefaultArgumentKind()) { case DefaultArgumentKind::Normal: { auto arg = param->getTypeCheckedDefaultExpr(); - SGM.preEmitFunction(constant, arg, f, arg); + auto loc = RegularLocation::getAutoGeneratedLocation(arg); + SGM.preEmitFunction(constant, arg, f, loc); PrettyStackTraceSILFunction X("silgen emitDefaultArgGenerator ", f); SILGenFunction SGF(SGM, *f, initDC); SGF.emitGeneratorFunction(constant, arg); @@ -740,7 +740,8 @@ static void emitDelayedFunction(SILGenModule &SGM, case DefaultArgumentKind::StoredProperty: { auto arg = param->getStoredProperty(); - SGM.preEmitFunction(constant, arg, f, arg); + auto loc = RegularLocation::getAutoGeneratedLocation(arg); + SGM.preEmitFunction(constant, arg, f, loc); PrettyStackTraceSILFunction X("silgen emitDefaultArgGenerator ", f); SILGenFunction SGF(SGM, *f, initDC); SGF.emitGeneratorFunction(constant, arg); @@ -772,12 +773,13 @@ static void emitDelayedFunction(SILGenModule &SGM, ->isPropertyMemberwiseInitializedWithWrappedType()) { auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); - assert(wrapperInfo.originalInitialValue); - init = wrapperInfo.originalInitialValue; + assert(wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()); + init = wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue(); } } - SGM.preEmitFunction(constant, init, f, init); + auto loc = RegularLocation::getAutoGeneratedLocation(init); + SGM.preEmitFunction(constant, init, f, loc); PrettyStackTraceSILFunction X("silgen emitStoredPropertyInitialization", f); f->createProfiler(init, constant, ForDefinition); SILGenFunction SGF(SGM, *f, initDC); @@ -800,7 +802,8 @@ static void emitDelayedFunction(SILGenModule &SGM, case SILDeclRef::Kind::PropertyWrapperBackingInitializer: { auto *var = cast(constant.getDecl()); - SGM.preEmitFunction(constant, var, f, var); + auto loc = RegularLocation::getAutoGeneratedLocation(var); + SGM.preEmitFunction(constant, var, f, loc); PrettyStackTraceSILFunction X( "silgen emitPropertyWrapperBackingInitializer", f); auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); @@ -822,7 +825,8 @@ static void emitDelayedFunction(SILGenModule &SGM, auto *onceToken = found->second.first; auto *onceFunc = found->second.second; - SGM.preEmitFunction(constant, global, f, global); + auto loc = RegularLocation::getAutoGeneratedLocation(global); + SGM.preEmitFunction(constant, global, f, loc); PrettyStackTraceSILFunction X("silgen emitGlobalAccessor", f); SILGenFunction(SGM, *f, global->getDeclContext()) .emitGlobalAccessor(global, onceToken, onceFunc); @@ -833,7 +837,8 @@ static void emitDelayedFunction(SILGenModule &SGM, case SILDeclRef::Kind::EnumElement: { auto *decl = cast(constant.getDecl()); - SGM.preEmitFunction(constant, decl, f, decl); + auto loc = RegularLocation::getAutoGeneratedLocation(decl); + SGM.preEmitFunction(constant, decl, f, loc); PrettyStackTraceSILFunction X("silgen enum constructor", f); SILGenFunction(SGM, *f, decl->getDeclContext()).emitEnumConstructor(decl); SGM.postEmitFunction(constant, f); @@ -935,43 +940,6 @@ void SILGenModule::postEmitFunction(SILDeclRef constant, emitDifferentiabilityWitnessesForFunction(constant, F); } -/// Returns the SIL differentiability witness generic signature given the -/// original declaration's generic signature and the derivative generic -/// signature. -/// -/// In general, the differentiability witness generic signature is equal to the -/// derivative generic signature. -/// -/// Edge case, if two conditions are satisfied: -/// 1. The derivative generic signature is equal to the original generic -/// signature. -/// 2. The derivative generic signature has *all concrete* generic parameters -/// (i.e. all generic parameters are bound to concrete types via same-type -/// requirements). -/// -/// Then the differentiability witness generic signature is `nullptr`. -/// -/// Both the original and derivative declarations are lowered to SIL functions -/// with a fully concrete type and no generic signature, so the -/// differentiability witness should similarly have no generic signature. -static GenericSignature -getDifferentiabilityWitnessGenericSignature(GenericSignature origGenSig, - GenericSignature derivativeGenSig) { - // If there is no derivative generic signature, return the original generic - // signature. - if (!derivativeGenSig) - return origGenSig; - // If derivative generic signature has all concrete generic parameters and is - // equal to the original generic signature, return `nullptr`. - auto derivativeCanGenSig = derivativeGenSig.getCanonicalSignature(); - auto origCanGenSig = origGenSig.getCanonicalSignature(); - if (origCanGenSig == derivativeCanGenSig && - derivativeCanGenSig->areAllParamsConcrete()) - return GenericSignature(); - // Otherwise, return the derivative generic signature. - return derivativeGenSig; -} - void SILGenModule::emitDifferentiabilityWitnessesForFunction( SILDeclRef constant, SILFunction *F) { // Visit `@derivative` attributes and generate SIL differentiability @@ -992,9 +960,10 @@ void SILGenModule::emitDifferentiabilityWitnessesForFunction( diffAttr->getDerivativeGenericSignature()) && "Type-checking should resolve derivative generic signatures for " "all original SIL functions with generic signatures"); - auto witnessGenSig = getDifferentiabilityWitnessGenericSignature( - AFD->getGenericSignature(), - diffAttr->getDerivativeGenericSignature()); + auto witnessGenSig = + autodiff::getDifferentiabilityWitnessGenericSignature( + AFD->getGenericSignature(), + diffAttr->getDerivativeGenericSignature()); AutoDiffConfig config(diffAttr->getParameterIndices(), resultIndices, witnessGenSig); emitDifferentiabilityWitness(AFD, F, config, /*jvp*/ nullptr, @@ -1015,8 +984,9 @@ void SILGenModule::emitDifferentiabilityWitnessesForFunction( auto origDeclRef = SILDeclRef(origAFD).asForeign(requiresForeignEntryPoint(origAFD)); auto *origFn = getFunction(origDeclRef, NotForDefinition); - auto witnessGenSig = getDifferentiabilityWitnessGenericSignature( - origAFD->getGenericSignature(), AFD->getGenericSignature()); + auto witnessGenSig = + autodiff::getDifferentiabilityWitnessGenericSignature( + origAFD->getGenericSignature(), AFD->getGenericSignature()); auto *resultIndices = IndexSubset::get(getASTContext(), 1, {0}); AutoDiffConfig config(derivAttr->getParameterIndices(), resultIndices, witnessGenSig); @@ -1279,7 +1249,8 @@ void SILGenModule::emitObjCAllocatorDestructor(ClassDecl *cd, auto ivarInitializer = SILDeclRef(cd, SILDeclRef::Kind::IVarInitializer) .asForeign(); SILFunction *f = getFunction(ivarInitializer, ForDefinition); - preEmitFunction(ivarInitializer, dd, f, dd); + auto loc = RegularLocation::getAutoGeneratedLocation(dd); + preEmitFunction(ivarInitializer, dd, f, loc); PrettyStackTraceSILFunction X("silgen emitDestructor ivar initializer", f); SILGenFunction(*this, *f, cd).emitIVarInitializer(ivarInitializer); postEmitFunction(ivarInitializer, f); @@ -1290,7 +1261,8 @@ void SILGenModule::emitObjCAllocatorDestructor(ClassDecl *cd, auto ivarDestroyer = SILDeclRef(cd, SILDeclRef::Kind::IVarDestroyer) .asForeign(); SILFunction *f = getFunction(ivarDestroyer, ForDefinition); - preEmitFunction(ivarDestroyer, dd, f, dd); + auto loc = RegularLocation::getAutoGeneratedLocation(dd); + preEmitFunction(ivarDestroyer, dd, f, loc); PrettyStackTraceSILFunction X("silgen emitDestructor ivar destroyer", f); SILGenFunction(*this, *f, cd).emitIVarDestroyer(ivarDestroyer); postEmitFunction(ivarDestroyer, f); @@ -1304,7 +1276,8 @@ void SILGenModule::emitDestructor(ClassDecl *cd, DestructorDecl *dd) { if (requiresIVarDestroyer(cd)) { SILDeclRef ivarDestroyer(cd, SILDeclRef::Kind::IVarDestroyer); SILFunction *f = getFunction(ivarDestroyer, ForDefinition); - preEmitFunction(ivarDestroyer, dd, f, dd); + auto loc = RegularLocation::getAutoGeneratedLocation(dd); + preEmitFunction(ivarDestroyer, dd, f, loc); PrettyStackTraceSILFunction X("silgen emitDestructor ivar destroyer", f); SILGenFunction(*this, *f, dd).emitIVarDestroyer(ivarDestroyer); postEmitFunction(ivarDestroyer, f); @@ -1321,10 +1294,10 @@ void SILGenModule::emitDestructor(ClassDecl *cd, DestructorDecl *dd) { if (dd->hasBody()) { SILDeclRef destroyer(dd, SILDeclRef::Kind::Destroyer); SILFunction *f = getFunction(destroyer, ForDefinition); - preEmitFunction(destroyer, dd, f, dd); + RegularLocation loc(dd); + preEmitFunction(destroyer, dd, f, loc); PrettyStackTraceSILFunction X("silgen emitDestroyingDestructor", f); SILGenFunction(*this, *f, dd).emitDestroyingDestructor(dd); - f->setDebugScope(new (M) SILDebugScope(dd, f)); postEmitFunction(destroyer, f); } @@ -1332,11 +1305,11 @@ void SILGenModule::emitDestructor(ClassDecl *cd, DestructorDecl *dd) { { SILDeclRef deallocator(dd, SILDeclRef::Kind::Deallocator); SILFunction *f = getFunction(deallocator, ForDefinition); - preEmitFunction(deallocator, dd, f, dd); + auto loc = RegularLocation::getAutoGeneratedLocation(dd); + preEmitFunction(deallocator, dd, f, loc); PrettyStackTraceSILFunction X("silgen emitDeallocatingDestructor", f); f->createProfiler(dd, deallocator, ForDefinition); SILGenFunction(*this, *f, dd).emitDeallocatingDestructor(dd); - f->setDebugScope(new (M) SILDebugScope(dd, f)); postEmitFunction(deallocator, f); } } @@ -1353,12 +1326,9 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, break; case DefaultArgumentKind::Inherited: - case DefaultArgumentKind::Column: - case DefaultArgumentKind::File: - case DefaultArgumentKind::FilePath: - case DefaultArgumentKind::Line: - case DefaultArgumentKind::Function: - case DefaultArgumentKind::DSOHandle: +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case DefaultArgumentKind::NAME: +#include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: @@ -1455,13 +1425,12 @@ void SILGenModule::emitObjCPropertyMethodThunks(AbstractStorageDecl *prop) { if (hasFunction(getterRef)) return; - RegularLocation ThunkBodyLoc(prop); - ThunkBodyLoc.markAutoGenerated(); + auto thunkBodyLoc = RegularLocation::getAutoGeneratedLocation(prop); // ObjC entry points are always externally usable, so emitting can't be // delayed. { SILFunction *f = getFunction(getterRef, ForDefinition); - preEmitFunction(getterRef, prop, f, ThunkBodyLoc); + preEmitFunction(getterRef, prop, f, thunkBodyLoc); PrettyStackTraceSILFunction X("silgen objc property getter thunk", f); f->setBare(IsBare); f->setThunk(IsThunk); @@ -1477,7 +1446,7 @@ void SILGenModule::emitObjCPropertyMethodThunks(AbstractStorageDecl *prop) { auto setterRef = SILDeclRef(setter, SILDeclRef::Kind::Func).asForeign(); SILFunction *f = getFunction(setterRef, ForDefinition); - preEmitFunction(setterRef, prop, f, ThunkBodyLoc); + preEmitFunction(setterRef, prop, f, thunkBodyLoc); PrettyStackTraceSILFunction X("silgen objc property setter thunk", f); f->setBare(IsBare); f->setThunk(IsThunk); @@ -1496,7 +1465,8 @@ void SILGenModule::emitObjCConstructorThunk(ConstructorDecl *constructor) { // delayed. SILFunction *f = getFunction(thunk, ForDefinition); - preEmitFunction(thunk, constructor, f, constructor); + auto loc = RegularLocation::getAutoGeneratedLocation(constructor); + preEmitFunction(thunk, constructor, f, loc); PrettyStackTraceSILFunction X("silgen objc constructor thunk", f); f->setBare(IsBare); f->setThunk(IsThunk); @@ -1512,7 +1482,8 @@ void SILGenModule::emitObjCDestructorThunk(DestructorDecl *destructor) { if (hasFunction(thunk)) return; SILFunction *f = getFunction(thunk, ForDefinition); - preEmitFunction(thunk, destructor, f, destructor); + auto loc = RegularLocation::getAutoGeneratedLocation(destructor); + preEmitFunction(thunk, destructor, f, loc); PrettyStackTraceSILFunction X("silgen objc destructor thunk", f); f->setBare(IsBare); f->setThunk(IsThunk); diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 0f3a72b595156..5e69eed1488d2 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -128,8 +128,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { ASTContext &getASTContext() { return M.getASTContext(); } - llvm::StringMap> - MagicFileStringsByFilePath; + llvm::StringMap> FileIDsByFilePath; static DeclName getMagicFunctionName(SILDeclRef ref); static DeclName getMagicFunctionName(DeclContext *dc); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 255bdeb4bafa9..bffecc2c2c187 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -131,8 +131,11 @@ getDynamicMethodLoweredType(SILModule &M, SILDeclRef constant, CanAnyFunctionType substMemberTy) { assert(constant.isForeign); - auto objcFormalTy = substMemberTy.withExtInfo(substMemberTy->getExtInfo() - .withSILRepresentation(SILFunctionTypeRepresentation::ObjCMethod)); + auto objcFormalTy = substMemberTy.withExtInfo( + substMemberTy->getExtInfo() + .intoBuilder() + .withSILRepresentation(SILFunctionTypeRepresentation::ObjCMethod) + .build()); return SILType::getPrimitiveObjectType( M.Types.getUncachedSILFunctionTypeForConstant( TypeExpansionContext::minimal(), constant, objcFormalTy)); @@ -315,7 +318,8 @@ class Callee { CanFunctionType SubstFormalInterfaceType; /// The substitutions applied to OrigFormalInterfaceType to produce - /// SubstFormalInterfaceType. + /// SubstFormalInterfaceType, substituted into the current type expansion + /// context. SubstitutionMap Substitutions; /// The list of values captured by our callee. @@ -351,14 +355,14 @@ class Callee { /// Constructor for Callee::forDirect. Callee(SILGenFunction &SGF, SILDeclRef standaloneFunction, AbstractionPattern origFormalType, CanAnyFunctionType substFormalType, - SubstitutionMap subs, SILLocation l, + SubstitutionMap subs, SubstitutionMap formalSubs, SILLocation l, bool callDynamicallyReplaceableImpl = false) : kind(callDynamicallyReplaceableImpl ? Kind::StandaloneFunctionDynamicallyReplaceableImpl : Kind::StandaloneFunction), Constant(standaloneFunction), OrigFormalInterfaceType(origFormalType), SubstFormalInterfaceType( - getSubstFormalInterfaceType(substFormalType, subs)), + getSubstFormalInterfaceType(substFormalType, formalSubs)), Substitutions(subs), Loc(l) {} /// Constructor called by all for* factory methods except forDirect and @@ -387,7 +391,9 @@ class Callee { auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); return Callee( SGF, c, ci.FormalPattern, ci.FormalType, - subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), + subs, + l, callPreviousDynamicReplaceableImpl); } @@ -1622,11 +1628,6 @@ static PreparedArguments emitStringLiteral(SILGenFunction &SGF, Expr *E, Length = Str.size(); break; - case StringLiteralExpr::UTF16: { - instEncoding = StringLiteralInst::Encoding::UTF16; - Length = unicode::getUTF16Length(Str); - break; - } case StringLiteralExpr::OneUnicodeScalar: { SILType Int32Ty = SILType::getBuiltinIntegerType(32, SGF.getASTContext()); SILValue UnicodeScalarValue = @@ -1668,11 +1669,6 @@ static PreparedArguments emitStringLiteral(SILGenFunction &SGF, Expr *E, ArrayRef Elts; ArrayRef TypeElts; switch (instEncoding) { - case StringLiteralInst::Encoding::UTF16: - Elts = llvm::makeArrayRef(EltsArray).slice(0, 2); - TypeElts = llvm::makeArrayRef(TypeEltsArray).slice(0, 2); - break; - case StringLiteralInst::Encoding::UTF8: Elts = EltsArray; TypeElts = TypeEltsArray; @@ -3857,7 +3853,7 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, auto emitter = specializedEmitter.getEarlyEmitter(); assert(uncurriedSites.size() == 1); - assert(!formalType->getExtInfo().throws()); + assert(!formalType->getExtInfo().isThrowing()); SILLocation uncurriedLoc = uncurriedSites[0].Loc; // We should be able to enforce that these arguments are @@ -4610,11 +4606,11 @@ StringRef SILGenFunction::getMagicFilePathString(SourceLoc loc) { return getSourceManager().getDisplayNameForLoc(loc); } -std::string SILGenFunction::getMagicFileString(SourceLoc loc) { +std::string SILGenFunction::getMagicFileIDString(SourceLoc loc) { auto path = getMagicFilePathString(loc); - auto result = SGM.MagicFileStringsByFilePath.find(path); - if (result != SGM.MagicFileStringsByFilePath.end()) + auto result = SGM.FileIDsByFilePath.find(path); + if (result != SGM.FileIDsByFilePath.end()) return std::get<0>(result->second); return path.str(); @@ -4848,8 +4844,9 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { auto magicLiteral = cast(literal); switch (magicLiteral->getKind()) { - case MagicIdentifierLiteralExpr::File: { - std::string value = loc.isValid() ? getMagicFileString(loc) : ""; + case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: + case MagicIdentifierLiteralExpr::FileID: { + std::string value = loc.isValid() ? getMagicFileIDString(loc) : ""; builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, magicLiteral->getStringEncoding()); builtinInit = magicLiteral->getBuiltinInitializer(); @@ -4857,6 +4854,7 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { break; } + case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: case MagicIdentifierLiteralExpr::FilePath: { StringRef value = loc.isValid() ? getMagicFilePathString(loc) : ""; builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, @@ -5436,7 +5434,7 @@ RValue SILGenFunction::emitGetAccessor(SILLocation loc, SILDeclRef get, subscriptIndices.emplace({}); emission.addCallSite(loc, std::move(subscriptIndices), - accessType->throws()); + accessType->isThrowing()); // T return emission.apply(c); @@ -5478,8 +5476,7 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, } } assert(values.isValid()); - emission.addCallSite(loc, std::move(values), - accessType->throws()); + emission.addCallSite(loc, std::move(values), accessType->isThrowing()); // () emission.apply(); } @@ -5513,7 +5510,7 @@ ManagedValue SILGenFunction::emitAddressorAccessor( subscriptIndices.emplace({}); emission.addCallSite(loc, std::move(subscriptIndices), - accessType->throws()); + accessType->isThrowing()); // Unsafe{Mutable}Pointer or // (Unsafe{Mutable}Pointer, Builtin.UnknownPointer) or @@ -5575,7 +5572,7 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, subscriptIndices.emplace({}); emission.addCallSite(loc, std::move(subscriptIndices), - accessType->throws()); + accessType->isThrowing()); auto endApplyHandle = emission.applyCoroutine(yields); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index fbff1a5b02d71..7eedc3b1975fa 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -574,7 +574,7 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, // context. Currently we don't capture anything directly into a block but a // Swift closure, but that's totally dumb. if (genericSig) - extInfo = extInfo.withIsPseudogeneric(); + extInfo = extInfo.intoBuilder().withIsPseudogeneric().build(); } auto invokeTy = SILFunctionType::get( diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index e81be5a9cefcc..0aef4de8034b3 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -230,8 +230,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // the property wrapper backing initializer. if (auto *wrappedVar = field->getOriginalWrappedProperty()) { auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); - if (wrappedInfo.originalInitialValue) { - auto arg = SGF.emitRValue(wrappedInfo.originalInitialValue); + auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + if (placeholder && placeholder->getOriginalWrappedValue()) { + auto arg = SGF.emitRValue(placeholder->getOriginalWrappedValue()); maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, std::move(arg)) .forwardInto(SGF, Loc, init.get()); @@ -274,8 +275,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // memberwise initialized, use the original wrapped value if it exists. if (auto *wrappedVar = field->getOriginalWrappedProperty()) { auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); - if (wrappedInfo.originalInitialValue) { - init = wrappedInfo.originalInitialValue; + auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + if (placeholder && placeholder->getOriginalWrappedValue()) { + init = placeholder->getOriginalWrappedValue(); } } @@ -937,9 +939,9 @@ static void emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, cast(pattern)->getSubPattern(), std::move(src)); - case PatternKind::Var: + case PatternKind::Binding: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), + cast(pattern)->getSubPattern(), std::move(src)); #define PATTERN(Name, Parent) diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 7a2815c4743af..953c3ae518556 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -144,7 +144,7 @@ auto SILGenFunction::emitSourceLocationArgs(SourceLoc sourceLoc, unsigned line = 0; unsigned column = 0; if (sourceLoc.isValid()) { - filename = getMagicFileString(sourceLoc); + filename = getMagicFileIDString(sourceLoc); std::tie(line, column) = ctx.SourceMgr.getPresumedLineAndColumnForLoc(sourceLoc); } diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 13e3dfc08d656..30415c5a1db1e 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1041,7 +1041,7 @@ struct InitializationForPattern InitializationPtr visitTypedPattern(TypedPattern *P) { return visit(P->getSubPattern()); } - InitializationPtr visitVarPattern(VarPattern *P) { + InitializationPtr visitBindingPattern(BindingPattern *P) { return visit(P->getSubPattern()); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 0596995c30b6e..0cd630a84180e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1868,7 +1868,7 @@ RValue SILGenFunction::emitAnyHashableErasure(SILLocation loc, auto convertFn = SGM.getConvertToAnyHashable(loc); if (!convertFn) return emitUndefRValue( - loc, getASTContext().getAnyHashableDecl()->getDeclaredType()); + loc, getASTContext().getAnyHashableDecl()->getDeclaredInterfaceType()); // Construct the substitution for T: Hashable. auto subMap = SubstitutionMap::getProtocolSubstitutions( @@ -2752,7 +2752,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, auto &C = SGM.getASTContext(); // Always take indexes parameter to match callee and caller signature on WebAssembly if (!indexes.empty() || C.LangOpts.Target.isOSBinFormatWasm()) - params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredType() + params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredInterfaceType() ->getCanonicalType(), ParameterConvention::Direct_Unowned}); @@ -2914,7 +2914,7 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, // indexes // Always take indexes parameter to match callee and caller signature on WebAssembly if (!indexes.empty() || C.LangOpts.Target.isOSBinFormatWasm()) - params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredType() + params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredInterfaceType() ->getCanonicalType(), ParameterConvention::Direct_Unowned}); @@ -3071,10 +3071,10 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, } auto &C = SGM.getASTContext(); - auto unsafeRawPointerTy = C.getUnsafeRawPointerDecl()->getDeclaredType() + auto unsafeRawPointerTy = C.getUnsafeRawPointerDecl()->getDeclaredInterfaceType() ->getCanonicalType(); - auto boolTy = C.getBoolDecl()->getDeclaredType()->getCanonicalType(); - auto intTy = C.getIntDecl()->getDeclaredType()->getCanonicalType(); + auto boolTy = C.getBoolDecl()->getDeclaredInterfaceType()->getCanonicalType(); + auto intTy = C.getIntDecl()->getDeclaredInterfaceType()->getCanonicalType(); auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); @@ -3765,6 +3765,11 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { case KeyPathExpr::Component::Kind::UnresolvedProperty: case KeyPathExpr::Component::Kind::UnresolvedSubscript: llvm_unreachable("not resolved"); + break; + + case KeyPathExpr::Component::Kind::DictionaryKey: + llvm_unreachable("DictionaryKey only valid in #keyPath"); + break; } } @@ -3802,12 +3807,12 @@ visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, SGFContext C) { RValue RValueEmitter:: visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C) { switch (E->getKind()) { - case MagicIdentifierLiteralExpr::File: - case MagicIdentifierLiteralExpr::FilePath: - case MagicIdentifierLiteralExpr::Function: - case MagicIdentifierLiteralExpr::Line: - case MagicIdentifierLiteralExpr::Column: +#define MAGIC_POINTER_IDENTIFIER(NAME, STRING, SYNTAX_KIND) +#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: +#include "swift/AST/MagicIdentifierKinds.def" return SGF.emitLiteral(E, C); + case MagicIdentifierLiteralExpr::DSOHandle: { auto SILLoc = SILLocation(E); auto UnsafeRawPointer = SGF.getASTContext().getUnsafeRawPointerDecl(); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index bacfbd3c9df08..d9acdc516f6ac 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -690,20 +690,18 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { SILParameterInfo(argv->getType().getASTType(), ParameterConvention::Direct_Unowned), }; - auto NSApplicationMainType = SILFunctionType::get(nullptr, - SILFunctionType::ExtInfo() - // Should be C calling convention, but NSApplicationMain - // has an overlay to fix the type of argv. - .withRepresentation(SILFunctionType::Representation::Thin), - SILCoroutineKind::None, - ParameterConvention::Direct_Unowned, - argTypes, - /*yields*/ {}, - SILResultInfo(argc->getType().getASTType(), - ResultConvention::Unowned), - /*error result*/ None, - SubstitutionMap(), SubstitutionMap(), - getASTContext()); + auto NSApplicationMainType = SILFunctionType::get( + nullptr, + SILFunctionType::ExtInfoBuilder() + // Should be C calling convention, but NSApplicationMain + // has an overlay to fix the type of argv. + .withRepresentation(SILFunctionType::Representation::Thin) + .build(), + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, argTypes, + /*yields*/ {}, + SILResultInfo(argc->getType().getASTType(), ResultConvention::Unowned), + /*error result*/ None, SubstitutionMap(), SubstitutionMap(), + getASTContext()); SILGenFunctionBuilder builder(SGM); auto NSApplicationMainFn = builder.getOrCreateFunction( @@ -805,9 +803,10 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, RegularLocation Loc(value); Loc.markAutoGenerated(); - // Default argument generators of function typed values return noescape - // functions. Strip the escape to noescape function conversion. - if (function.kind == SILDeclRef::Kind::DefaultArgGenerator) { + // If a default argument or stored property initializer value is a noescape + // function type, strip the escape to noescape function conversion. + if (function.kind == SILDeclRef::Kind::DefaultArgGenerator || + function.kind == SILDeclRef::Kind::StoredPropertyInitializer) { if (auto funType = value->getType()->getAs()) { if (funType->getExtInfo().isNoEscape()) { auto conv = cast(value); @@ -852,7 +851,8 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, auto var = cast(function.getDecl()); auto wrappedInfo = var->getPropertyWrapperBackingPropertyInfo(); auto param = params->get(0); - opaqueValue.emplace(*this, wrappedInfo.underlyingValue, + auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + opaqueValue.emplace(*this, placeholder->getOpaqueValuePlaceholder(), maybeEmitValueOfLocalVarDecl(param)); assert(value == wrappedInfo.initializeFromOriginal); @@ -881,6 +881,11 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { if (auto originalProperty = var->getOriginalWrappedProperty()) { if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) { interfaceType = originalProperty->getPropertyWrapperInitValueInterfaceType(); + + if (auto fnType = interfaceType->getAs()) { + auto newExtInfo = fnType->getExtInfo().withNoEscape(false); + interfaceType = fnType->withExtInfo(newExtInfo); + } } } @@ -966,8 +971,10 @@ void SILGenFunction::emitProfilerIncrement(ASTNode N) { B.createIntegerLiteral(Loc, Int64Ty, SP->getPGOFuncHash()), B.createIntegerLiteral(Loc, Int32Ty, SP->getNumRegionCounters()), B.createIntegerLiteral(Loc, Int32Ty, CounterIt->second)}; - B.createBuiltin(Loc, C.getIdentifier("int_instrprof_increment"), - SGM.Types.getEmptyTupleType(), {}, Args); + B.createBuiltin( + Loc, + C.getIdentifier(getBuiltinName(BuiltinValueKind::IntInstrprofIncrement)), + SGM.Types.getEmptyTupleType(), {}, Args); } ProfileCounter SILGenFunction::loadProfilerCount(ASTNode Node) const { diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index dbdc8fb8d8b28..b5e98c78bda87 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -567,7 +567,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction Optional getUnknownEnforcement(VarDecl *var = nullptr); SourceManager &getSourceManager() { return SGM.M.getASTContext().SourceMgr; } - std::string getMagicFileString(SourceLoc loc); + std::string getMagicFileIDString(SourceLoc loc); StringRef getMagicFilePathString(SourceLoc loc); StringRef getMagicFunctionString(); diff --git a/lib/SILGen/SILGenGlobalVariable.cpp b/lib/SILGen/SILGenGlobalVariable.cpp index 75d13470da839..551b438f461f0 100644 --- a/lib/SILGen/SILGenGlobalVariable.cpp +++ b/lib/SILGen/SILGenGlobalVariable.cpp @@ -141,7 +141,7 @@ struct GenGlobalAccessors : public PatternVisitor void visitTypedPattern(TypedPattern *P) { return visit(P->getSubPattern()); } - void visitVarPattern(VarPattern *P) { + void visitBindingPattern(BindingPattern *P) { return visit(P->getSubPattern()); } void visitTuplePattern(TuplePattern *P) { diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index da8d723b8d4b4..522f89f02f47b 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1335,10 +1335,7 @@ namespace { // the argument types of the setter and initializer shall be // different, so we don't rewrite an assignment into an // initialization. - if (VD->isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) - return false; - - return true; + return !wrapperInfo.wrappedValuePlaceholder->isAutoClosure(); } return false; diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index bdec97860688a..b7ee169fc9faf 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -99,7 +99,7 @@ static void dumpPattern(const Pattern *p, llvm::raw_ostream &os) { case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: llvm_unreachable("not semantic"); } } @@ -129,7 +129,7 @@ static bool isDirectlyRefutablePattern(const Pattern *p) { // Recur into simple wrapping patterns. case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: return isDirectlyRefutablePattern(p->getSemanticsProvidingPattern()); } llvm_unreachable("bad pattern"); @@ -190,7 +190,7 @@ static unsigned getNumSpecializationsRecursive(const Pattern *p, unsigned n) { // Recur into simple wrapping patterns. case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: return getNumSpecializationsRecursive(p->getSemanticsProvidingPattern(), n); } llvm_unreachable("bad pattern"); @@ -231,7 +231,7 @@ static bool isWildcardPattern(const Pattern *p) { // Recur into simple wrapping patterns. case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: return isWildcardPattern(p->getSemanticsProvidingPattern()); } @@ -293,7 +293,7 @@ static Pattern *getSimilarSpecializingPattern(Pattern *p, Pattern *first) { } case PatternKind::Paren: - case PatternKind::Var: + case PatternKind::Binding: case PatternKind::Typed: llvm_unreachable("not semantic"); } @@ -1348,7 +1348,7 @@ void PatternMatchEmission::emitSpecializedDispatch(ClauseMatrix &clauses, case PatternKind::Paren: case PatternKind::Typed: - case PatternKind::Var: + case PatternKind::Binding: llvm_unreachable("non-semantic pattern kind!"); case PatternKind::Tuple: diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index d78dacc61df28..0ee11d74f6439 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3099,11 +3099,12 @@ CanSILFunctionType SILGenFunction::buildThunkType( // This may inherit @noescape from the expectedType. The @noescape attribute // is only stripped when using this type to materialize a new decl. - auto extInfo = expectedType->getExtInfo() - .withRepresentation(SILFunctionType::Representation::Thin); + auto extInfoBuilder = + expectedType->getExtInfo().intoBuilder().withRepresentation( + SILFunctionType::Representation::Thin); if (withoutActuallyEscaping) - extInfo = extInfo.withNoEscape(false); + extInfoBuilder = extInfoBuilder.withNoEscape(false); // Does the thunk type involve archetypes other than opened existentials? bool hasArchetypes = false; @@ -3185,7 +3186,7 @@ CanSILFunctionType SILGenFunction::buildThunkType( // pseudogeneric, since we have no way to pass generic parameters. if (genericSig) if (F.getLoweredFunctionType()->isPseudogeneric()) - extInfo = extInfo.withIsPseudogeneric(); + extInfoBuilder = extInfoBuilder.withIsPseudogeneric(); // Add the function type as the parameter. auto contextConvention = @@ -3243,14 +3244,12 @@ CanSILFunctionType SILGenFunction::buildThunkType( } // The type of the thunk function. - return SILFunctionType::get(genericSig, extInfo, - expectedType->getCoroutineKind(), - ParameterConvention::Direct_Unowned, - interfaceParams, interfaceYields, - interfaceResults, interfaceErrorResult, - expectedType->getPatternSubstitutions(), - SubstitutionMap(), - getASTContext()); + return SILFunctionType::get( + genericSig, extInfoBuilder.build(), expectedType->getCoroutineKind(), + ParameterConvention::Direct_Unowned, interfaceParams, interfaceYields, + interfaceResults, interfaceErrorResult, + expectedType->getPatternSubstitutions(), SubstitutionMap(), + getASTContext()); } static ManagedValue createPartialApplyOfThunk(SILGenFunction &SGF, @@ -3477,8 +3476,9 @@ static CanSILFunctionType buildWithoutActuallyEscapingThunkType( CanSILFunctionType &escapingType, GenericEnvironment *&genericEnv, SubstitutionMap &interfaceSubs, CanType &dynamicSelfType) { - assert(escapingType->getExtInfo() == - noEscapingType->getExtInfo().withNoEscape(false)); + assert(escapingType->getExtInfo().isEqualTo( + noEscapingType->getExtInfo().withNoEscape(false), + useClangTypes(escapingType))); CanType inputSubstType, outputSubstType; auto type = SGF.buildThunkType(noEscapingType, escapingType, @@ -3540,8 +3540,9 @@ SILGenFunction::createWithoutActuallyEscapingClosure( auto noEscapingFnSubstTy = noEscapingFunctionValue.getType() .castTo(); // TODO: maybe this should use a more explicit instruction. - assert(escapingFnSubstTy->getExtInfo() == noEscapingFnSubstTy->getExtInfo() - .withNoEscape(false)); + assert(escapingFnSubstTy->getExtInfo().isEqualTo( + noEscapingFnSubstTy->getExtInfo().withNoEscape(false), + useClangTypes(escapingFnSubstTy))); // Apply function type substitutions, since the code sequence for a thunk // doesn't vary with function representation. @@ -4131,12 +4132,13 @@ ManagedValue Transform::transformFunction(ManagedValue fn, } // We do not, conversion is trivial. - auto expectedEI = expectedFnType->getExtInfo(); + auto expectedEI = expectedFnType->getExtInfo().intoBuilder(); auto newEI = expectedEI.withRepresentation(fnType->getRepresentation()) .withNoEscape(fnType->getRepresentation() == SILFunctionType::Representation::Thick ? fnType->isNoEscape() - : expectedFnType->isNoEscape()); + : expectedFnType->isNoEscape()) + .build(); auto newFnType = adjustFunctionType(expectedFnType, newEI, fnType->getCalleeConvention(), fnType->getWitnessMethodConformanceOrInvalid()); diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp index 20d2ee6e4c9d1..90caebc1b3310 100644 --- a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -230,14 +230,17 @@ transformCalleeStorage(const StorageAccessInfo &storage, case AccessedStorage::Global: // Global accesses is universal. return storage; - case AccessedStorage::Class: { + case AccessedStorage::Class: + case AccessedStorage::Tail: { // If the object's value is an argument, translate it into a value on the // caller side. SILValue obj = storage.getObject(); if (auto *arg = dyn_cast(obj)) { SILValue argVal = getCallerArg(fullApply, arg->getIndex()); if (argVal) { - unsigned idx = storage.getPropertyIndex(); + unsigned idx = (storage.getKind() == AccessedStorage::Class) + ? storage.getPropertyIndex() + : AccessedStorage::TailIndex; // Remap this storage info. The argument source value is now the new // object. The old storage info is inherited. return StorageAccessInfo(AccessedStorage::forClass(argVal, idx), @@ -253,7 +256,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); if (argVal) { // Remap the argument source value and inherit the old storage info. - auto calleeStorage = findAccessedStorageNonNested(argVal); + auto calleeStorage = findAccessedStorage(argVal); if (calleeStorage) return StorageAccessInfo(calleeStorage, storage); } @@ -262,7 +265,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, // // This is an untested bailout. It is only reachable if the call graph // contains an edge that getCallerArg is unable to analyze OR if - // findAccessedStorageNonNested returns an invalid SILValue, which won't + // findAccessedStorage returns an invalid SILValue, which won't // pass SIL verification. // // FIXME: In case argVal is invalid, support Unidentified access for invalid @@ -299,7 +302,7 @@ void AccessedStorageResult::visitBeginAccess(B *beginAccess) { return; const AccessedStorage &storage = - findAccessedStorageNonNested(beginAccess->getSource()); + findAccessedStorage(beginAccess->getSource()); if (storage.getKind() == AccessedStorage::Unidentified) { // This also catches invalid storage. diff --git a/lib/SILOptimizer/Differentiation/ADContext.cpp b/lib/SILOptimizer/Differentiation/ADContext.cpp index d6591cd92e228..575f6afc968b0 100644 --- a/lib/SILOptimizer/Differentiation/ADContext.cpp +++ b/lib/SILOptimizer/Differentiation/ADContext.cpp @@ -123,10 +123,24 @@ DifferentiableFunctionInst *ADContext::createDifferentiableFunction( return dfi; } +LinearFunctionInst *ADContext::createLinearFunction( + SILBuilder &builder, SILLocation loc, IndexSubset *parameterIndices, + SILValue original, Optional transposeFunction) { + auto *lfi = builder.createLinearFunction(loc, parameterIndices, original, + transposeFunction); + processedLinearFunctionInsts.erase(lfi); + return lfi; +} + DifferentiableFunctionExpr * ADContext::findDifferentialOperator(DifferentiableFunctionInst *inst) { return inst->getLoc().getAsASTNode(); } +LinearFunctionExpr * +ADContext::findDifferentialOperator(LinearFunctionInst *inst) { + return inst->getLoc().getAsASTNode(); +} + } // end namespace autodiff } // end namespace swift diff --git a/lib/SILOptimizer/Differentiation/Common.cpp b/lib/SILOptimizer/Differentiation/Common.cpp index 5b507a765bc7d..20cdd11a781fe 100644 --- a/lib/SILOptimizer/Differentiation/Common.cpp +++ b/lib/SILOptimizer/Differentiation/Common.cpp @@ -452,8 +452,11 @@ findMinimalDerivativeConfiguration(AbstractFunctionDecl *original, silParameterIndices->getNumIndices() < minimalConfig->parameterIndices->getNumIndices())) { minimalASTParameterIndices = config.parameterIndices; - minimalConfig = AutoDiffConfig(silParameterIndices, config.resultIndices, - config.derivativeGenericSignature); + minimalConfig = + AutoDiffConfig(silParameterIndices, config.resultIndices, + autodiff::getDifferentiabilityWitnessGenericSignature( + original->getGenericSignature(), + config.derivativeGenericSignature)); } } return minimalConfig; diff --git a/lib/SILOptimizer/Differentiation/DifferentiationInvoker.cpp b/lib/SILOptimizer/Differentiation/DifferentiationInvoker.cpp index aeb7fb43b474a..db3e8db7f92ae 100644 --- a/lib/SILOptimizer/Differentiation/DifferentiationInvoker.cpp +++ b/lib/SILOptimizer/Differentiation/DifferentiationInvoker.cpp @@ -28,6 +28,8 @@ SourceLoc DifferentiationInvoker::getLocation() const { switch (kind) { case Kind::DifferentiableFunctionInst: return getDifferentiableFunctionInst()->getLoc().getSourceLoc(); + case Kind::LinearFunctionInst: + return getLinearFunctionInst()->getLoc().getSourceLoc(); case Kind::IndirectDifferentiation: return getIndirectDifferentiation().first->getLoc().getSourceLoc(); case Kind::SILDifferentiabilityWitnessInvoker: @@ -46,6 +48,9 @@ void DifferentiationInvoker::print(llvm::raw_ostream &os) const { os << "differentiable_function_inst=(" << *getDifferentiableFunctionInst() << ")"; break; + case Kind::LinearFunctionInst: + os << "linear_function_inst=(" << *getLinearFunctionInst() << ")"; + break; case Kind::IndirectDifferentiation: { auto indDiff = getIndirectDifferentiation(); os << "indirect_differentiation=(" << *std::get<0>(indDiff) << ')'; diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index e860562c9b2da..8152f409d7592 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -601,7 +601,7 @@ class JVPCloner::Implementation final builder, loc, indices.parameters, indices.results, origCallee); // Record the `differentiable_function` instruction. - context.addDifferentiableFunctionInstToWorklist(diffFuncInst); + context.getDifferentiableFunctionInstWorklist().push_back(diffFuncInst); auto borrowedADFunc = builder.emitBeginBorrowOperation(loc, diffFuncInst); auto extractedJVP = builder.createDifferentiableFunctionExtract( @@ -749,7 +749,15 @@ class JVPCloner::Implementation final // instruction to the `differentiable_function` worklist. TypeSubstCloner::visitDifferentiableFunctionInst(dfi); auto *newDFI = cast(getOpValue(dfi)); - context.addDifferentiableFunctionInstToWorklist(newDFI); + context.getDifferentiableFunctionInstWorklist().push_back(newDFI); + } + + void visitLinearFunctionInst(LinearFunctionInst *lfi) { + // Clone `linear_function` from original to JVP, then add the cloned + // instruction to the `linear_function` worklist. + TypeSubstCloner::visitLinearFunctionInst(lfi); + auto *newLFI = cast(getOpValue(lfi)); + context.getLinearFunctionInstWorklist().push_back(newLFI); } //--------------------------------------------------------------------------// diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index cc2f80b8c44ae..0438285ed07aa 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -1459,8 +1459,21 @@ class PullbackCloner::Implementation final assert(getRemappedTangentType(urci->getOperand()->getType()) == getRemappedTangentType(urci->getType()) && "Operand/result must have the same `TangentVector` type"); - auto adj = getAdjointValue(bb, urci); - addAdjointValue(bb, urci->getOperand(), adj, urci->getLoc()); + switch (getTangentValueCategory(urci)) { + case SILValueCategory::Object: { + auto adj = getAdjointValue(bb, urci); + addAdjointValue(bb, urci->getOperand(), adj, urci->getLoc()); + break; + } + case SILValueCategory::Address: { + auto &adjDest = getAdjointBuffer(bb, urci); + auto destType = remapType(adjDest->getType()); + addToAdjointBuffer(bb, urci->getOperand(), adjDest, urci->getLoc()); + builder.emitDestroyAddrAndFold(urci->getLoc(), adjDest); + emitZeroIndirect(destType.getASTType(), adjDest, urci->getLoc()); + break; + } + } } /// Handle `upcast` instruction. @@ -1473,8 +1486,21 @@ class PullbackCloner::Implementation final assert(getRemappedTangentType(ui->getOperand()->getType()) == getRemappedTangentType(ui->getType()) && "Operand/result must have the same `TangentVector` type"); - auto adj = getAdjointValue(bb, ui); - addAdjointValue(bb, ui->getOperand(), adj, ui->getLoc()); + switch (getTangentValueCategory(ui)) { + case SILValueCategory::Object: { + auto adj = getAdjointValue(bb, ui); + addAdjointValue(bb, ui->getOperand(), adj, ui->getLoc()); + break; + } + case SILValueCategory::Address: { + auto &adjDest = getAdjointBuffer(bb, ui); + auto destType = remapType(adjDest->getType()); + addToAdjointBuffer(bb, ui->getOperand(), adjDest, ui->getLoc()); + builder.emitDestroyAddrAndFold(ui->getLoc(), adjDest); + emitZeroIndirect(destType.getASTType(), adjDest, ui->getLoc()); + break; + } + } } #define NOT_DIFFERENTIABLE(INST, DIAG) void visit##INST##Inst(INST##Inst *inst); @@ -2797,7 +2823,7 @@ AllocStackInst *PullbackCloner::Implementation::getArrayAdjointElementBuffer( auto *eltIndexLiteral = builder.createIntegerLiteral(loc, builtinIntType, eltIndex); auto intType = SILType::getPrimitiveObjectType( - ctx.getIntDecl()->getDeclaredType()->getCanonicalType()); + ctx.getIntDecl()->getDeclaredInterfaceType()->getCanonicalType()); // %index_int = struct $Int (%index_literal) auto *eltIndexInt = builder.createStruct(loc, intType, {eltIndexLiteral}); auto *swiftModule = getModule().getSwiftModule(); diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index ce7f3955d1792..820805fff06d9 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -129,13 +129,14 @@ CanSILFunctionType buildThunkType(SILFunction *fn, // - Building a reabstraction thunk type. // - Building an index subset thunk type, where the expected type has context // (i.e. is `@convention(thick)`). - auto extInfo = expectedType->getExtInfo(); + auto extInfoBuilder = expectedType->getExtInfo().intoBuilder(); if (thunkKind == DifferentiationThunkKind::Reabstraction || - extInfo.hasContext()) { - extInfo = extInfo.withRepresentation(SILFunctionType::Representation::Thin); + extInfoBuilder.hasContext()) { + extInfoBuilder = extInfoBuilder.withRepresentation( + SILFunctionType::Representation::Thin); } if (withoutActuallyEscaping) - extInfo = extInfo.withNoEscape(false); + extInfoBuilder = extInfoBuilder.withNoEscape(false); // Does the thunk type involve archetypes other than opened existentials? bool hasArchetypes = false; @@ -191,7 +192,7 @@ CanSILFunctionType buildThunkType(SILFunction *fn, // pseudogeneric, since we have no way to pass generic parameters. if (genericSig) if (fn->getLoweredFunctionType()->isPseudogeneric()) - extInfo = extInfo.withIsPseudogeneric(); + extInfoBuilder = extInfoBuilder.withIsPseudogeneric(); // Add the function type as the parameter. auto contextConvention = @@ -241,7 +242,7 @@ CanSILFunctionType buildThunkType(SILFunction *fn, // The type of the thunk function. return SILFunctionType::get( - genericSig, extInfo, expectedType->getCoroutineKind(), + genericSig, extInfoBuilder.build(), expectedType->getCoroutineKind(), ParameterConvention::Direct_Unowned, interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, expectedType->getPatternSubstitutions(), SubstitutionMap(), diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index 933b706baff57..ef3980436d9d1 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -529,7 +529,7 @@ class VJPCloner::Implementation final getBuilder(), loc, indices.parameters, indices.results, origCallee); // Record the `differentiable_function` instruction. - context.addDifferentiableFunctionInstToWorklist(diffFuncInst); + context.getDifferentiableFunctionInstWorklist().push_back(diffFuncInst); auto borrowedADFunc = builder.emitBeginBorrowOperation(loc, diffFuncInst); auto extractedVJP = getBuilder().createDifferentiableFunctionExtract( @@ -623,7 +623,15 @@ class VJPCloner::Implementation final // instruction to the `differentiable_function` worklist. TypeSubstCloner::visitDifferentiableFunctionInst(dfi); auto *newDFI = cast(getOpValue(dfi)); - context.addDifferentiableFunctionInstToWorklist(newDFI); + context.getDifferentiableFunctionInstWorklist().push_back(newDFI); + } + + void visitLinearFunctionInst(LinearFunctionInst *lfi) { + // Clone `linear_function` from original to VJP, then add the cloned + // instruction to the `linear_function` worklist. + TypeSubstCloner::visitLinearFunctionInst(lfi); + auto *newLFI = cast(getOpValue(lfi)); + context.getLinearFunctionInstWorklist().push_back(newLFI); } }; diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp index 89ec8381ec722..b1decdc3830da 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -75,10 +75,6 @@ class ExistentialSpecializer : public SILFunctionTransform { return; } - // FIXME: This pass should be able to support ownership. - if (F->hasOwnership()) - return; - /// Get CallerAnalysis information handy. CA = PM->getAnalysis(); @@ -220,13 +216,6 @@ bool ExistentialSpecializer::canSpecializeCalleeFunction(FullApplySite &Apply) { if (!Callee->isDefinition()) return false; - // If the callee has ownership enabled, bail. - // - // FIXME: We should be able to handle callees that have ownership, but the - // pass has not been updated yet. - if (Callee->hasOwnership()) - return false; - // Ignore generic functions. Generic functions should be fully specialized // before attempting to introduce new generic parameters for existential // arguments. Otherwise, there's no guarantee that the generic specializer diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 6971002bdfdda..66617c63b0c24 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -107,8 +107,13 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() { SILBuilderWithScope Builder(exitBB->getTerminator()); // A return location can't be used for a non-return instruction. auto loc = RegularLocation::getAutoGeneratedLocation(); - for (SILValue cleanupVal : CleanupValues) - Builder.createDestroyAddr(loc, cleanupVal); + for (SILValue cleanupVal : CleanupValues) { + if (cleanupVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + Builder.emitEndBorrowOperation(loc, cleanupVal); + } else { + Builder.emitDestroyOperation(loc, cleanupVal); + } + } for (auto *ASI : llvm::reverse(AllocStackInsts)) Builder.createDeallocStack(loc, ASI); @@ -180,10 +185,10 @@ void ExistentialSpecializerCloner::cloneArguments( NewF.getLoweredType(NewF.mapTypeIntoContext(GenericParam)); GenericSILType = GenericSILType.getCategoryType( ArgDesc.Arg->getType().getCategory()); - auto *NewArg = - ClonedEntryBB->createFunctionArgument(GenericSILType, ArgDesc.Decl); - NewArg->setOwnershipKind(ValueOwnershipKind( - NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention())); + auto *NewArg = ClonedEntryBB->createFunctionArgument( + GenericSILType, ArgDesc.Decl, + ValueOwnershipKind(NewF, GenericSILType, + ArgDesc.Arg->getArgumentConvention())); // Determine the Conformances. SILType ExistentialType = ArgDesc.Arg->getType().getObjectType(); CanType OpenedType = NewArg->getType().getASTType(); @@ -224,15 +229,22 @@ void ExistentialSpecializerCloner::cloneArguments( } case ExistentialRepresentation::Class: { SILValue NewArgValue = NewArg; + bool origConsumed = EAD.isConsumed; + + // Load our object if needed and if our original value was not consumed, + // make a copy in ossa. Do not perturb code-gen in non-ossa code though. if (!NewArg->getType().isObject()) { - NewArgValue = NewFBuilder.createLoad(InsertLoc, NewArg, - LoadOwnershipQualifier::Unqualified); + auto qual = LoadOwnershipQualifier::Take; + if (NewFBuilder.hasOwnership() && !origConsumed) { + qual = LoadOwnershipQualifier::Copy; + } + NewArgValue = + NewFBuilder.emitLoadValueOperation(InsertLoc, NewArg, qual); + } else { + if (NewFBuilder.hasOwnership() && !origConsumed) { + NewArgValue = NewFBuilder.emitCopyValueOperation(InsertLoc, NewArg); + } } - - // FIXME_ownership: init_existential_ref always takes ownership of the - // incoming reference. If the argument convention is borrowed - // (!isConsumed), then we should create a copy_value here and add this new - // existential to the CleanupValues vector. /// Simple case: Create an init_existential. /// %5 = init_existential_ref %0 : $T : $T, $P @@ -240,19 +252,31 @@ void ExistentialSpecializerCloner::cloneArguments( InsertLoc, ArgDesc.Arg->getType().getObjectType(), NewArg->getType().getASTType(), NewArgValue, Conformances); - + + // If we don't have an object and we are in ossa, the store will consume + // the InitRef. if (!NewArg->getType().isObject()) { auto alloc = NewFBuilder.createAllocStack(InsertLoc, InitRef->getType()); - NewFBuilder.createStore(InsertLoc, InitRef, alloc, - StoreOwnershipQualifier::Unqualified); + NewFBuilder.emitStoreValueOperation(InsertLoc, InitRef, alloc, + StoreOwnershipQualifier::Init); InitRef = alloc; AllocStackInsts.push_back(alloc); + } else { + // Otherwise in ossa, we need to add init existential ref as something + // to be cleaned up. In non-ossa, we do not insert the copies, so we do + // not need to do it then. + // + // TODO: This would be simpler if we had managed value/cleanup scopes. + if (NewFBuilder.hasOwnership() && !origConsumed) { + CleanupValues.push_back(InitRef); + } } entryArgs.push_back(InitRef); break; } + default: { llvm_unreachable("Unhandled existential type in ExistentialTransform!"); break; @@ -451,12 +475,13 @@ void ExistentialTransform::populateThunkBody() { SILValue archetypeValue; auto ExistentialRepr = ArgDesc.Arg->getType().getPreferredExistentialRepresentation(); + bool OriginallyConsumed = ETAD.isConsumed; switch (ExistentialRepr) { case ExistentialRepresentation::Opaque: { archetypeValue = Builder.createOpenExistentialAddr( Loc, OrigOperand, OpenedSILType, it->second.AccessType); SILValue calleeArg = archetypeValue; - if (ETAD.isConsumed) { + if (OriginallyConsumed) { // open_existential_addr projects a borrowed address into the // existential box. Since the callee consumes the generic value, we // must pass in a copy. @@ -474,19 +499,39 @@ void ExistentialTransform::populateThunkBody() { // If the operand is not object type, we need an explicit load. SILValue OrigValue = OrigOperand; if (!OrigOperand->getType().isObject()) { - OrigValue = Builder.createLoad(Loc, OrigValue, - LoadOwnershipQualifier::Unqualified); + auto qual = LoadOwnershipQualifier::Take; + if (Builder.hasOwnership() && !OriginallyConsumed) { + qual = LoadOwnershipQualifier::Copy; + } + OrigValue = Builder.emitLoadValueOperation(Loc, OrigValue, qual); + } else { + if (Builder.hasOwnership() && !OriginallyConsumed) { + OrigValue = Builder.emitCopyValueOperation(Loc, OrigValue); + } } + // OpenExistentialRef forwards ownership, so it does the right thing // regardless of whether the argument is borrowed or consumed. archetypeValue = Builder.createOpenExistentialRef(Loc, OrigValue, OpenedSILType); + + // If we don't have an object and we are in ossa, the store will consume + // the open_existential_ref. if (!OrigOperand->getType().isObject()) { SILValue ASI = Builder.createAllocStack(Loc, OpenedSILType); - Builder.createStore(Loc, archetypeValue, ASI, - StoreOwnershipQualifier::Unqualified); + Builder.emitStoreValueOperation(Loc, archetypeValue, ASI, + StoreOwnershipQualifier::Init); Temps.push_back({ASI, SILValue()}); archetypeValue = ASI; + } else { + // Otherwise in ossa, we need to add open_existential_ref as something + // to be cleaned up. In non-ossa, we do not insert the copies, so we + // do not need to do it then. + // + // TODO: This would be simpler if we had managed value/cleanup scopes. + if (Builder.hasOwnership() && !OriginallyConsumed) { + Temps.push_back({SILValue(), archetypeValue}); + } } ApplyArgs.push_back(archetypeValue); break; @@ -572,11 +617,14 @@ void ExistentialTransform::populateThunkBody() { // copy_addr %valAdr to %temp // <== Temp CopyAddr // apply(%temp) // <== Temp is consumed by the apply // - // Destroy the original argument and deallocation the temporary: + // Destroy the original argument and deallocation the temporary. If we have + // an address this becomes: // destroy_addr %consumedExistential : $*Protocol // dealloc_stack %temp : $*T + // + // Otherwise, if we had an object, we just emit a destroy_value. if (Temp.DestroyValue) - Builder.createDestroyAddr(cleanupLoc, Temp.DestroyValue); + Builder.emitDestroyOperation(cleanupLoc, Temp.DestroyValue); if (Temp.DeallocStackEntry) Builder.createDeallocStack(cleanupLoc, Temp.DeallocStackEntry); } diff --git a/lib/SILOptimizer/IPO/CMakeLists.txt b/lib/SILOptimizer/IPO/CMakeLists.txt index e746c95dec22f..f9324eaafa21a 100644 --- a/lib/SILOptimizer/IPO/CMakeLists.txt +++ b/lib/SILOptimizer/IPO/CMakeLists.txt @@ -4,7 +4,6 @@ target_sources(swiftSILOptimizer PRIVATE ClosureSpecializer.cpp CrossModuleSerializationSetup.cpp DeadFunctionElimination.cpp - EagerSpecializer.cpp GlobalOpt.cpp GlobalPropertyOpt.cpp LetPropertiesOpts.cpp diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 515c312cdab90..113d71ccb32b8 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -431,9 +431,8 @@ bool SILGlobalOpt::optimizeInitializer(SILFunction *AddrF, // If the addressor contains a single "once" call, it calls globalinit_func, // and the globalinit_func is called by "once" from a single location, // continue; otherwise bail. - auto *InitF = findInitializer(Module, AddrF, CallToOnce); - if (!InitF || !InitF->getName().startswith("globalinit_") || - InitializerCount[InitF] > 1) + auto *InitF = findInitializer(AddrF, CallToOnce); + if (!InitF || InitializerCount[InitF] > 1) return false; // If the globalinit_func is trivial, continue; otherwise bail. diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index a70f93d764c29..79e5bd6d96b8e 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -700,19 +700,18 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, InstSet &SideEffectInsts, AccessedStorageAnalysis *ASA, DominanceInfo *DT) { - const AccessedStorage &storage = - findAccessedStorageNonNested(BI->getSource()); + const AccessedStorage &storage = findAccessedStorage(BI->getSource()); if (!storage) { return false; } - auto BIAccessedStorageNonNested = findAccessedStorageNonNested(BI); + auto BIAccessedStorageNonNested = findAccessedStorage(BI); auto safeBeginPred = [&](BeginAccessInst *OtherBI) { if (BI == OtherBI) { return true; } return BIAccessedStorageNonNested.isDistinctFrom( - findAccessedStorageNonNested(OtherBI)); + findAccessedStorage(OtherBI)); }; if (!std::all_of(BeginAccesses.begin(), BeginAccesses.end(), safeBeginPred)) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index b5b24f9581bd5..f27984e9ec524 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -583,16 +583,6 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, .highlight(NoteAccess.getAccessLoc().getSourceRange()); } -/// Look through a value to find the underlying storage accessed. -static AccessedStorage findValidAccessedStorage(SILValue Source) { - const AccessedStorage &Storage = findAccessedStorage(Source); - if (!Storage) { - llvm::dbgs() << "Bad memory access source: " << Source; - llvm_unreachable("Unexpected access source."); - } - return Storage; -} - /// Returns true when the apply calls the Standard Library swap(). /// Used for fix-its to suggest replacing with Collection.swapAt() /// on exclusivity violations. @@ -700,7 +690,7 @@ struct AccessState { // Find conflicting access on each argument using AccessSummaryAnalysis. static void -checkCaptureAccess(ApplySite Apply, AccessState &State, +checkAccessSummary(ApplySite Apply, AccessState &State, const AccessSummaryAnalysis::FunctionSummary &FS) { for (unsigned ArgumentIndex : range(Apply.getNumArguments())) { @@ -721,7 +711,7 @@ checkCaptureAccess(ApplySite Apply, AccessState &State, // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = findValidAccessedStorage(Argument); + const AccessedStorage &Storage = identifyCapturedStorage(Argument); auto AccessIt = State.Accesses->find(Storage); // Are there any accesses in progress at the time of the call? @@ -743,7 +733,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // dynamically replaceable. SILFunction *Callee = Apply.getCalleeFunction(); if (Callee && !Callee->empty()) { - checkCaptureAccess(Apply, State, State.ASA->getOrCreateSummary(Callee)); + checkAccessSummary(Apply, State, State.ASA->getOrCreateSummary(Callee)); return; } // In the absence of AccessSummaryAnalysis, conservatively assume by-address @@ -755,7 +745,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = findValidAccessedStorage(argOper.get()); + const AccessedStorage &Storage = identifyCapturedStorage(argOper.get()); // Are there any accesses in progress at the time of the call? auto AccessIt = State.Accesses->find(Storage); @@ -845,8 +835,8 @@ static void checkForViolationsAtInstruction(SILInstruction &I, return; SILAccessKind Kind = BAI->getAccessKind(); - const AccessedStorage &Storage = - findValidAccessedStorage(BAI->getSource()); + const AccessedStorage &Storage = identifyFormalAccess(BAI); + assert(Storage && "unidentified formal access"); // Storage may be associated with a nested access where the outer access is // "unsafe". That's ok because the outer access can itself be treated like a // valid source, as long as we don't ask for its source. @@ -862,14 +852,15 @@ static void checkForViolationsAtInstruction(SILInstruction &I, } if (auto *EAI = dyn_cast(&I)) { - if (EAI->getBeginAccess()->getEnforcement() == SILAccessEnforcement::Unsafe) + BeginAccessInst *BAI = EAI->getBeginAccess(); + if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe) return; - auto It = - State.Accesses->find(findValidAccessedStorage(EAI->getSource())); + const AccessedStorage &Storage = identifyFormalAccess(BAI); + assert(Storage && "unidentified formal access"); + auto It = State.Accesses->find(identifyFormalAccess(BAI)); AccessInfo &Info = It->getSecond(); - BeginAccessInst *BAI = EAI->getBeginAccess(); const IndexTrieNode *SubPath = State.ASA->findSubPathAccessed(BAI); Info.endAccess(EAI, SubPath); @@ -973,7 +964,7 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, // Check that the given address-type operand is guarded by begin/end access // markers. static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { - SILValue address = memOper->get(); + SILValue address = getAddressAccess(memOper->get()); SILInstruction *memInst = memOper->getUser(); auto error = [address, memInst]() { @@ -985,6 +976,21 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { abort(); }; + // Check if this address is guarded by an access. + if (auto *BAI = dyn_cast(address)) { + if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe) + return; + + const AccessedStorage &Storage = identifyFormalAccess(BAI); + assert(Storage && "unidentified formal access"); + AccessInfo &Info = Accesses[Storage]; + if (Info.hasAccessesInProgress()) + return; + + error(); + } + // --- This address is not guarded by a begin_access. + // If the memory instruction is only used for initialization, it doesn't need // an access marker. if (memInstMustInitialize(memOper)) @@ -1011,10 +1017,9 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { return; } - // Strip off address projections, but not ref_element_addr. const AccessedStorage &storage = findAccessedStorage(address); // findAccessedStorage may return an invalid storage object if the address - // producer is not recognized by its whitelist. For the purpose of + // producer is not recognized by its allowlist. For the purpose of // verification, we assume that this can only happen for local // initialization, not a formal memory access. The strength of // verification rests on the completeness of the opcode list inside @@ -1040,18 +1045,6 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { return; } - // Otherwise, the address base should be an in-scope begin_access. - if (storage.getKind() == AccessedStorage::Nested) { - auto *BAI = cast(storage.getValue()); - if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe) - return; - - const AccessedStorage &Storage = findValidAccessedStorage(BAI->getSource()); - AccessInfo &Info = Accesses[Storage]; - if (!Info.hasAccessesInProgress()) - error(); - return; - } error(); } diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 0c323052e339f..5156ffac9e792 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -57,6 +57,11 @@ using llvm::SmallDenseSet; using llvm::SmallMapVector; using llvm::SmallSet; +/// This flag enables experimental `@differentiable(linear)` function +/// transposition. +static llvm::cl::opt EnableExperimentalLinearMapTransposition( + "enable-experimental-linear-map-transposition", llvm::cl::init(false)); + /// This flag is used to disable `differentiable_function_extract` instruction /// folding for SIL testing purposes. static llvm::cl::opt SkipFoldingDifferentiableFunctionExtraction( @@ -89,6 +94,12 @@ class DifferentiationTransformer { SILBuilder &builder, SILLocation loc, DifferentiationInvoker invoker); + /// Given a `linear_function` instruction that is missing a transpose operand, + /// return a new `linear_function` instruction with the transpose filled in. + SILValue promoteToLinearFunction(LinearFunctionInst *inst, + SILBuilder &builder, SILLocation loc, + DifferentiationInvoker invoker); + public: /// Construct an `DifferentiationTransformer` for the given module. explicit DifferentiationTransformer(SILModuleTransform &transform) @@ -113,6 +124,10 @@ class DifferentiationTransformer { /// missing derivative functions if necessary. bool processDifferentiableFunctionInst(DifferentiableFunctionInst *dfi); + /// Process the given `linear_function` instruction, filling in the missing + /// transpose function if necessary. + bool processLinearFunctionInst(LinearFunctionInst *lfi); + /// Fold `differentiable_function_extract` users of the given /// `differentiable_function` instruction, directly replacing them with /// `differentiable_function` instruction operands. If the @@ -408,7 +423,7 @@ static SILValue reapplyFunctionConversion( "one argument"); auto *dfi = context.createDifferentiableFunction( builder, loc, parameterIndices, resultIndices, newArgs.back()); - context.addDifferentiableFunctionInstToWorklist(dfi); + context.getDifferentiableFunctionInstWorklist().push_back(dfi); newArgs.back() = dfi; } // Compute substitution map for reapplying `partial_apply`. @@ -771,8 +786,8 @@ static SILFunction *createEmptyVJP(ADContext &context, SILFunction *original, witness->getConfig())) .str(); CanGenericSignature vjpCanGenSig; - if (auto jvpGenSig = witness->getDerivativeGenericSignature()) - vjpCanGenSig = jvpGenSig->getCanonicalSignature(); + if (auto vjpGenSig = witness->getDerivativeGenericSignature()) + vjpCanGenSig = vjpGenSig->getCanonicalSignature(); GenericEnvironment *vjpGenericEnv = nullptr; if (vjpCanGenSig && !vjpCanGenSig->areAllParamsConcrete()) vjpGenericEnv = vjpCanGenSig->getGenericEnvironment(); @@ -857,9 +872,7 @@ static void emitFatalError(ADContext &context, SILFunction *f, ResultConvention::Unowned); // Fatal error function must have type `@convention(thin) () -> Never`. auto fatalErrorFnType = SILFunctionType::get( - /*genericSig*/ nullptr, - SILFunctionType::ExtInfo().withRepresentation( - SILFunctionTypeRepresentation::Thin), + /*genericSig*/ nullptr, SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, {}, /*interfaceYields*/ {}, neverResultInfo, /*interfaceErrorResults*/ None, {}, {}, context.getASTContext()); @@ -1005,8 +1018,10 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( // Construct new curry thunk type with `@differentiable` function // result. auto diffResultFnTy = resultFnTy->getWithExtInfo( - resultFnTy->getExtInfo().withDifferentiabilityKind( - DifferentiabilityKind::Normal)); + resultFnTy->getExtInfo() + .intoBuilder() + .withDifferentiabilityKind(DifferentiabilityKind::Normal) + .build()); auto newThunkResult = thunkResult.getWithInterfaceType(diffResultFnTy); auto thunkType = SILFunctionType::get( thunkTy->getSubstGenericSignature(), thunkTy->getExtInfo(), @@ -1046,7 +1061,7 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( retInst->eraseFromParent(); context.recordGeneratedFunction(newThunk); - context.addDifferentiableFunctionInstToWorklist(dfi); + context.getDifferentiableFunctionInstWorklist().push_back(dfi); if (dt.processDifferentiableFunctionInst(dfi)) return nullptr; } @@ -1198,10 +1213,31 @@ SILValue DifferentiationTransformer::promoteToDifferentiableFunction( auto *newDiffFn = context.createDifferentiableFunction( builder, loc, parameterIndices, resultIndices, origFnCopy, std::make_pair(derivativeFns[0], derivativeFns[1])); - context.addDifferentiableFunctionInstToWorklist(dfi); + context.getDifferentiableFunctionInstWorklist().push_back(dfi); return newDiffFn; } +SILValue DifferentiationTransformer::promoteToLinearFunction( + LinearFunctionInst *lfi, SILBuilder &builder, SILLocation loc, + DifferentiationInvoker invoker) { + // Note: for now, this function creates a new `linear_function` instruction + // with an undef transpose function operand. Eventually, a legitimate + // transpose function operand should be created and used. + auto origFnOperand = lfi->getOriginalFunction(); + auto origFnCopy = builder.emitCopyValueOperation(loc, origFnOperand); + auto *parameterIndices = lfi->getParameterIndices(); + auto originalType = origFnOperand->getType().castTo(); + auto transposeFnType = originalType->getAutoDiffTransposeFunctionType( + parameterIndices, context.getTypeConverter(), + LookUpConformanceInModule(builder.getModule().getSwiftModule())); + auto transposeType = SILType::getPrimitiveObjectType(transposeFnType); + auto transposeFn = SILUndef::get(transposeType, builder.getFunction()); + auto *newLinearFn = context.createLinearFunction( + builder, loc, parameterIndices, origFnCopy, SILValue(transposeFn)); + context.getLinearFunctionInstWorklist().push_back(lfi); + return newLinearFn; +} + /// Fold `differentiable_function_extract` users of the given /// `differentiable_function` instruction, directly replacing them with /// `differentiable_function` instruction operands. If the @@ -1288,6 +1324,39 @@ bool DifferentiationTransformer::processDifferentiableFunctionInst( return false; } +bool DifferentiationTransformer::processLinearFunctionInst( + LinearFunctionInst *lfi) { + PrettyStackTraceSILNode dfiTrace("canonicalizing `linear_function`", + cast(lfi)); + PrettyStackTraceSILFunction fnTrace("...in", lfi->getFunction()); + LLVM_DEBUG({ + auto &s = getADDebugStream() << "Processing LinearFunctoinInst:\n"; + lfi->printInContext(s); + }); + + // If `lfi` already has a transpose function, do not process. + if (lfi->hasTransposeFunction()) + return false; + + SILFunction *parent = lfi->getFunction(); + auto loc = lfi->getLoc(); + SILBuilderWithScope builder(lfi); + auto linearFnValue = promoteToLinearFunction(lfi, builder, loc, lfi); + // Mark `lfi` as processed so that it won't be reprocessed after deletion. + context.markLinearFunctionInstAsProcessed(lfi); + if (!linearFnValue) + return true; + // Replace all uses of `lfi`. + lfi->replaceAllUsesWith(linearFnValue); + // Destroy the original operand. + builder.emitDestroyValueOperation(loc, lfi->getOriginalFunction()); + lfi->eraseFromParent(); + + transform.invalidateAnalysis(parent, + SILAnalysis::InvalidationKind::FunctionBody); + return false; +} + /// Automatic differentiation transform entry. void Differentiation::run() { auto &module = *getModule(); @@ -1308,22 +1377,26 @@ void Differentiation::run() { context.addInvoker(&witness); } - // Register all the `differentiable_function` instructions in the module that - // trigger differentiation. + // Register all the `differentiable_function` and `linear_function` + // instructions in the module that trigger differentiation. for (SILFunction &f : module) { for (SILBasicBlock &bb : f) { for (SILInstruction &i : bb) { - if (auto *dfi = dyn_cast(&i)) - context.addDifferentiableFunctionInstToWorklist(dfi); - // Reject uncanonical `linear_function` instructions. - // FIXME(SR-11850): Add support for linear map transposition. - else if (auto *lfi = dyn_cast(&i)) { - if (!lfi->hasTransposeFunction()) { - astCtx.Diags.diagnose( + if (auto *dfi = dyn_cast(&i)) { + context.getDifferentiableFunctionInstWorklist().push_back(dfi); + } else if (auto *lfi = dyn_cast(&i)) { + // If linear map transposition is not enable and an uncanonical + // `linear_function` instruction is encounter, emit a diagnostic. + // FIXME(SR-11850): Finish support for linear map transposition. + if (!EnableExperimentalLinearMapTransposition) { + if (!lfi->hasTransposeFunction()) { + astCtx.Diags.diagnose( lfi->getLoc().getSourceLoc(), diag::autodiff_conversion_to_linear_function_not_supported); - errorOccurred = true; + errorOccurred = true; + } } + context.getLinearFunctionInstWorklist().push_back(lfi); } } } @@ -1331,7 +1404,8 @@ void Differentiation::run() { // If nothing has triggered differentiation, there's nothing to do. if (context.getInvokers().empty() && - context.isDifferentiableFunctionInstsWorklistEmpty()) + context.getDifferentiableFunctionInstWorklist().empty() && + context.getLinearFunctionInstWorklist().empty()) return; // Differentiation relies on the stdlib (the Swift module). @@ -1346,8 +1420,9 @@ void Differentiation::run() { if (!context.getInvokers().empty()) { loc = context.getInvokers().front().second.getLocation(); } else { - assert(!context.isDifferentiableFunctionInstsWorklistEmpty()); - loc = context.popDifferentiableFunctionInstFromWorklist() + assert(!context.getDifferentiableFunctionInstWorklist().empty()); + loc = context.getDifferentiableFunctionInstWorklist() + .pop_back_val() ->getLoc() .getSourceLoc(); } @@ -1368,13 +1443,23 @@ void Differentiation::run() { } // Iteratively process `differentiable_function` instruction worklist. - while (auto *dfi = context.popDifferentiableFunctionInstFromWorklist()) { + while (!context.getDifferentiableFunctionInstWorklist().empty()) { + auto *dfi = context.getDifferentiableFunctionInstWorklist().pop_back_val(); // Skip instructions that have been already been processed. if (context.isDifferentiableFunctionInstProcessed(dfi)) continue; errorOccurred |= transformer.processDifferentiableFunctionInst(dfi); } + // Iteratively process `linear_function` instruction worklist. + while (!context.getLinearFunctionInstWorklist().empty()) { + auto *lfi = context.getLinearFunctionInstWorklist().pop_back_val(); + // Skip instructions that have been already been processed. + if (context.isLinearFunctionInstProcessed(lfi)) + continue; + errorOccurred |= transformer.processLinearFunctionInst(lfi); + } + // If any error occurred while processing witnesses or // `differentiable_function` instructions, clean up. if (errorOccurred) { diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index bf72ae76224cb..9b3c498058d5e 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -222,7 +222,7 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) { // Perform classic SSA optimizations for cleanup. P.addLowerAggregateInstrs(); P.addSILCombine(); - P.addSROA(); + P.addEarlySROA(); P.addMem2Reg(); P.addDCE(); P.addSILCombine(); @@ -290,7 +290,11 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addLowerAggregateInstrs(); // Split up operations on stack-allocated aggregates (struct, tuple). - P.addSROA(); + if (OpLevel == OptimizationLevelKind::HighLevel) { + P.addEarlySROA(); + } else { + P.addSROA(); + } // Promote stack allocations to values. P.addMem2Reg(); @@ -489,6 +493,8 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { addFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); + + P.addStringOptimization(); } // After "high-level" function passes have processed the entire call tree, run @@ -524,6 +530,7 @@ static void addSerializePipeline(SILPassPipelinePlan &P) { static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) { P.startPipeline("MidLevel,Function", true /*isFunctionPassPipeline*/); + addFunctionPasses(P, OptimizationLevelKind::MidLevel); // Specialize partially applied functions with dead arguments as a preparation @@ -660,6 +667,11 @@ static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { // Only has an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); + + // Only has an effect if opt-remark is enabled. + P.addOptRemarkGenerator(); + + P.addPruneVTables(); } static void addSILDebugInfoGeneratorPipeline(SILPassPipelinePlan &P) { diff --git a/lib/SILOptimizer/PassManager/SILOptimizerRequests.cpp b/lib/SILOptimizer/PassManager/SILOptimizerRequests.cpp index 67db81ac7e5c1..67fe0cf2c3a78 100644 --- a/lib/SILOptimizer/PassManager/SILOptimizerRequests.cpp +++ b/lib/SILOptimizer/PassManager/SILOptimizerRequests.cpp @@ -57,6 +57,37 @@ swift::extractNearestSourceLoc(const SILPipelineExecutionDescriptor &desc) { return extractNearestSourceLoc(desc.SM); } +//----------------------------------------------------------------------------// +// LoweredSILRequest computation. +//----------------------------------------------------------------------------// + +std::unique_ptr +LoweredSILRequest::evaluate(Evaluator &evaluator, + ASTLoweringDescriptor desc) const { + auto silMod = llvm::cantFail(evaluator(ASTLoweringRequest{desc})); + silMod->installSILRemarkStreamer(); + silMod->setSerializeSILAction([]() {}); + + runSILDiagnosticPasses(*silMod); + + { + FrontendStatsTracer tracer(silMod->getASTContext().Stats, + "SIL verification, pre-optimization"); + silMod->verify(); + } + + runSILOptimizationPasses(*silMod); + + { + FrontendStatsTracer tracer(silMod->getASTContext().Stats, + "SIL verification, post-optimization"); + silMod->verify(); + } + + runSILLoweringPasses(*silMod); + return silMod; +} + // Define request evaluation functions for each of the SILGen requests. static AbstractRequestFunction *silOptimizerRequestFunctions[] = { #define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \ diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index 92f0958f82b98..e2042b4435b21 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -240,6 +240,16 @@ bool SILCombiner::runOnFunction(SILFunction &F) { return Changed; } +void SILCombiner::eraseInstIncludingUsers(SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + while (!result->use_empty()) { + eraseInstIncludingUsers(result->use_begin()->getUser()); + } + } + eraseInstFromFunction(*inst); +} + + //===----------------------------------------------------------------------===// // Entry Points //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 6630010e5b7ae..44669e43fb2b0 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -147,6 +147,10 @@ class SILCombiner : return nullptr; } + // Erases \p inst and all of its users, recursively. + // The caller has to make sure that all users are removable (e.g. dead). + void eraseInstIncludingUsers(SILInstruction *inst); + SILInstruction *eraseInstFromFunction(SILInstruction &I, bool AddOperandsToWorklist = true) { SILBasicBlock::iterator nullIter; diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 9aa6581052f3f..fd5d3a012b555 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -654,12 +654,6 @@ bool SILCombiner::eraseApply(FullApplySite FAS, const UserListTy &Users) { return true; } -SILInstruction * -SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) { - // String literals concatenation optimizer. - return tryToConcatenateStrings(AI, Builder); -} - /// This routine replaces the old witness method inst with a new one. void SILCombiner::replaceWitnessMethodInst( WitnessMethodInst *WMI, SILBuilderContext &BuilderCtx, CanType ConcreteType, @@ -1464,12 +1458,6 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { } if (SF) { - if (SF->getEffectsKind() < EffectsKind::ReleaseNone) { - // Try to optimize string concatenation. - if (auto I = optimizeConcatenationOfStringLiterals(AI)) { - return I; - } - } if (SF->hasSemanticsAttr(semantics::ARRAY_UNINITIALIZED)) { UserListTy Users; // If the uninitialized array is only written into then it can be removed. diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index 86081cf98d946..4230ceaa37d28 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -337,12 +337,53 @@ SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) { return eraseInstFromFunction(*URCI); } +template +static bool canBeUsedAsCastDestination(SILValue value, CastInst *castInst, + DominanceAnalysis *DA) { + return value && + value->getType() == castInst->getTargetLoweredType().getObjectType() && + DA->get(castInst->getFunction())->properlyDominates(value, castInst); +} + + SILInstruction * SILCombiner:: visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *UCCAI) { if (UCCAI->getFunction()->hasOwnership()) return nullptr; + // Optimize the unconditional_checked_cast_addr in this pattern: + // + // %box = alloc_existential_box $Error, $ConcreteError + // %a = project_existential_box $ConcreteError in %b : $Error + // store %value to %a : $*ConcreteError + // %err = alloc_stack $Error + // store %box to %err : $*Error + // %dest = alloc_stack $ConcreteError + // unconditional_checked_cast_addr Error in %err : $*Error to + // ConcreteError in %dest : $*ConcreteError + // + // to: + // ... + // retain_value %value : $ConcreteError + // destroy_addr %err : $*Error + // store %value to %dest $*ConcreteError + // + // This lets the alloc_existential_box become dead and it can be removed in + // following optimizations. + SILValue val = getConcreteValueOfExistentialBoxAddr(UCCAI->getSrc(), UCCAI); + if (canBeUsedAsCastDestination(val, UCCAI, DA)) { + SILBuilderContext builderCtx(Builder.getModule(), Builder.getTrackingList()); + SILBuilderWithScope builder(UCCAI, builderCtx); + SILLocation loc = UCCAI->getLoc(); + builder.createRetainValue(loc, val, builder.getDefaultAtomicity()); + builder.createDestroyAddr(loc, UCCAI->getSrc()); + builder.createStore(loc, val, UCCAI->getDest(), + StoreOwnershipQualifier::Unqualified); + return eraseInstFromFunction(*UCCAI); + } + + // Perform the purly type-based cast optimization. if (CastOpt.optimizeUnconditionalCheckedCastAddrInst(UCCAI)) MadeChange = true; @@ -522,6 +563,62 @@ visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI) { if (CCABI->getFunction()->hasOwnership()) return nullptr; + // Optimize the checked_cast_addr_br in this pattern: + // + // %box = alloc_existential_box $Error, $ConcreteError + // %a = project_existential_box $ConcreteError in %b : $Error + // store %value to %a : $*ConcreteError + // %err = alloc_stack $Error + // store %box to %err : $*Error + // %dest = alloc_stack $ConcreteError + // checked_cast_addr_br Error in %err : $*Error to + // ConcreteError in %dest : $*ConcreteError, success_bb, failing_bb + // + // to: + // ... + // retain_value %value : $ConcreteError + // destroy_addr %err : $*Error // if consumption-kind is take + // store %value to %dest $*ConcreteError + // br success_bb + // + // This lets the alloc_existential_box become dead and it can be removed in + // following optimizations. + // + // TODO: Also handle the WillFail case. + SILValue val = getConcreteValueOfExistentialBoxAddr(CCABI->getSrc(), CCABI); + if (canBeUsedAsCastDestination(val, CCABI, DA)) { + SILBuilderContext builderCtx(Builder.getModule(), Builder.getTrackingList()); + SILBuilderWithScope builder(CCABI, builderCtx); + SILLocation loc = CCABI->getLoc(); + builder.createRetainValue(loc, val, builder.getDefaultAtomicity()); + switch (CCABI->getConsumptionKind()) { + case CastConsumptionKind::TakeAlways: + case CastConsumptionKind::TakeOnSuccess: + builder.createDestroyAddr(loc, CCABI->getSrc()); + break; + case CastConsumptionKind::CopyOnSuccess: + break; + case CastConsumptionKind::BorrowAlways: + llvm_unreachable("BorrowAlways is not supported on addresses"); + } + builder.createStore(loc, val, CCABI->getDest(), + StoreOwnershipQualifier::Unqualified); + + // Replace the cast with a constant conditional branch. + // Don't just create an unconditional branch to not change the CFG in + // SILCombine. SimplifyCFG will clean that up. + // + // Another possibility would be to run this optimization in SimplifyCFG. + // But this has other problems, like it's more difficult to reason about a + // consistent dominator tree in SimplifyCFG. + SILType boolTy = SILType::getBuiltinIntegerType(1, builder.getASTContext()); + auto *trueVal = builder.createIntegerLiteral(loc, boolTy, 1); + builder.createCondBranch(loc, trueVal, CCABI->getSuccessBB(), + CCABI->getFailureBB()); + return eraseInstFromFunction(*CCABI); + } + + // Perform the purly type-based cast optimization. if (CastOpt.optimizeCheckedCastAddrBranchInst(CCABI)) MadeChange = true; diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index e476210f9d48c..c499356dfbea5 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -55,66 +55,40 @@ SILCombiner::visitAllocExistentialBoxInst(AllocExistentialBoxInst *AEBI) { // debug_value %6#0 : $Error // strong_release %6#0 : $Error - StoreInst *SingleStore = nullptr; - StrongReleaseInst *SingleRelease = nullptr; - ProjectExistentialBoxInst *SingleProjection = nullptr; - - // For each user U of the alloc_existential_box... - for (auto U : getNonDebugUses(AEBI)) { - - if (auto *PEBI = dyn_cast(U->getUser())) { - if (SingleProjection) return nullptr; - SingleProjection = PEBI; - for (auto AddrUse : getNonDebugUses(PEBI)) { - // Record stores into the box. - if (auto *SI = dyn_cast(AddrUse->getUser())) { - // If this is not the only store into the box then bail out. - if (SingleStore) return nullptr; - SingleStore = SI; - continue; - } - // If there are other users to the box value address then bail out. - return nullptr; - } - continue; - } + SILValue boxedValue = + getConcreteValueOfExistentialBox(AEBI, /*ignoreUser*/ nullptr); + if (!boxedValue) + return nullptr; - // Record releases of the box. - if (auto *RI = dyn_cast(U->getUser())) { + // Check if the box is released at a single place. That's the end of its + // lifetime. + StrongReleaseInst *singleRelease = nullptr; + for (Operand *use : AEBI->getUses()) { + if (auto *RI = dyn_cast(use->getUser())) { // If this is not the only release of the box then bail out. - if (SingleRelease) return nullptr; - SingleRelease = RI; - continue; + if (singleRelease) + return nullptr; + singleRelease = RI; } - - // If there are other users to the box then bail out. - return nullptr; - } - - if (SingleStore && SingleRelease) { - assert(SingleProjection && "store without a projection"); - // Release the value that was stored into the existential box. The box - // is going away so we need to release the stored value. - // NOTE: It's important that the release is inserted at the single - // release of the box and not at the store, because a balancing retain could - // be _after_ the store, e.g: - // %box = alloc_existential_box - // %addr = project_existential_box %box - // store %value to %addr - // retain_value %value // must insert the release after this retain - // strong_release %box - Builder.setInsertionPoint(SingleRelease); - Builder.createReleaseValue(AEBI->getLoc(), SingleStore->getSrc(), - SingleRelease->getAtomicity()); - - // Erase the instruction that stores into the box and the release that - // releases the box, and finally, release the box. - eraseInstFromFunction(*SingleRelease); - eraseInstFromFunction(*SingleStore); - eraseInstFromFunction(*SingleProjection); - return eraseInstFromFunction(*AEBI); } + if (!singleRelease) + return nullptr; + // Release the value that was stored into the existential box. The box + // is going away so we need to release the stored value. + // NOTE: It's important that the release is inserted at the single + // release of the box and not at the store, because a balancing retain could + // be _after_ the store, e.g: + // %box = alloc_existential_box + // %addr = project_existential_box %box + // store %value to %addr + // retain_value %value // must insert the release after this retain + // strong_release %box + Builder.setInsertionPoint(singleRelease); + Builder.createReleaseValue(AEBI->getLoc(), boxedValue, + singleRelease->getAtomicity()); + + eraseInstIncludingUsers(AEBI); return nullptr; } diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp index d454493053f8f..06fefeeac81c9 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp @@ -251,7 +251,7 @@ void DominatedAccessAnalysis::analyzeAccess(BeginAccessInst *BAI, // Only track dynamic access in the result. Static accesses still need to be // tracked by data flow, but they can't be optimized as "dominating". if (BAI->getEnforcement() == SILAccessEnforcement::Dynamic) { - AccessedStorage storage = findAccessedStorageNonNested(BAI->getSource()); + AccessedStorage storage = findAccessedStorage(BAI->getSource()); // Copy the AccessStorage into DomAccessedStorage. All pass-specific bits // are initialized to zero. domStorage = DomAccessedStorage(storage); @@ -381,7 +381,7 @@ void DominatedAccessRemoval::visitBeginAccess(BeginAccessInst *BAI) { return; // Only track "identifiable" storage. - if (currDomStorage.isUniquelyIdentifiedOrClass()) { + if (currDomStorage.isFormalAccessBase()) { if (checkDominatedAccess(BAI, currDomStorage)) return; } @@ -569,7 +569,7 @@ void DominatedAccessRemoval::tryInsertLoopPreheaderAccess( // Insert the new dominating instruction in both DominatedAccessAnalysis and // storageToDomMap if it has uniquely identifiable storage. - if (!currAccessInfo.isUniquelyIdentifiedOrClass()) + if (!currAccessInfo.isFormalAccessBase()) return; AccessedStorage storage = static_cast(currAccessInfo); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp index 8aadfb4ce1f93..ef4777e1441d6 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp @@ -566,8 +566,7 @@ bool AccessConflictAndMergeAnalysis::identifyBeginAccesses() { // now, since this optimization runs at the end of the pipeline, we // gracefully ignore unrecognized source address patterns, which show up // here as an invalid `storage` value. - AccessedStorage storage = - findAccessedStorageNonNested(beginAccess->getSource()); + AccessedStorage storage = findAccessedStorage(beginAccess->getSource()); auto iterAndInserted = storageSet.insert(storage); @@ -744,6 +743,7 @@ void AccessConflictAndMergeAnalysis::visitMayRelease(SILInstruction *instr, // accesses can be affected by a deinitializer. auto isHeapAccess = [](AccessedStorage::Kind accessKind) { return accessKind == AccessedStorage::Class + || accessKind == AccessedStorage::Class || accessKind == AccessedStorage::Global; }; // Mark the in-scope accesses as having a nested conflict diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp index 0ff7b4c2f1d9b..026391691a3ee 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp @@ -139,6 +139,7 @@ static bool isBarrier(SILInstruction *inst) { case BuiltinValueKind::TypePtrAuthDiscriminator: case BuiltinValueKind::GlobalStringTablePointer: case BuiltinValueKind::COWBufferForReading: + case BuiltinValueKind::IntInstrprofIncrement: return false; // Handle some rare builtins that may be sensitive to object lifetime diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 65df81551ad4f..36c82f0f676cc 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -42,7 +42,7 @@ /// /// Warning: This is only sound when unidentified accesses can never alias with /// Class/Global access. To enforce this, the SILVerifier calls -/// findAccessedStorage() for every access, which asserts that any Unidentified +/// identifyFormalAccess() for every access, which asserts that any Unidentified /// access belongs to a know pattern that cannot originate from Class or Global /// accesses. /// @@ -70,7 +70,7 @@ using llvm::SmallDenseSet; // AccessedStorage. Returns nullptr for any storage that can't be partitioned // into a disjoint location. // -// findAccessedStorage may only return Unidentified storage for a global +// identifyFormalAccess may only return Unidentified storage for a global // variable access if the global is defined in a different module. // // WARNING: Retrieving VarDecl for Class access is not constant time. @@ -85,6 +85,7 @@ const VarDecl *getDisjointAccessLocation(const AccessedStorage &storage) { } case AccessedStorage::Box: case AccessedStorage::Stack: + case AccessedStorage::Tail: case AccessedStorage::Argument: case AccessedStorage::Yield: case AccessedStorage::Unidentified: @@ -103,7 +104,7 @@ namespace { // // The existence of unidentified access complicates this problem. For this // optimization to be valid, Global and Class property access must always be -// identifiable. findAccessedStorage() in MemAccessUtils enforces a short list +// identifiable. identifyFormalAccess() in MemAccessUtils enforces a short list // of unidentified producers (non-address PhiArgument, PointerToAddress, Undef, // & local-init). We cannot allow the address of a global variable or class // property to be exposed via one of these instructions, unless the declaration @@ -168,13 +169,13 @@ void GlobalAccessRemoval::perform() { void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { if (auto *BAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorageNonNested(BAI->getSource()); + AccessedStorage storage = findAccessedStorage(BAI->getSource()); const VarDecl *decl = getDisjointAccessLocation(storage); recordAccess(BAI, decl, storage.getKind(), BAI->hasNoNestedConflict()); return; } if (auto *BUAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorageNonNested(BUAI->getSource()); + AccessedStorage storage = findAccessedStorage(BUAI->getSource()); const VarDecl *decl = getDisjointAccessLocation(storage); recordAccess(BUAI, decl, storage.getKind(), BUAI->hasNoNestedConflict()); return; diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index 932a2e264e3a5..c51043a1910c3 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -19,10 +19,12 @@ target_sources(swiftSILOptimizer PRIVATE DestroyHoisting.cpp Devirtualizer.cpp DifferentiabilityWitnessDevirtualizer.cpp + EagerSpecializer.cpp GenericSpecializer.cpp MergeCondFail.cpp Outliner.cpp ObjectOutliner.cpp + OptRemarkGenerator.cpp PerformanceInliner.cpp PhiArgumentOptimizations.cpp PruneVTables.cpp @@ -38,6 +40,7 @@ target_sources(swiftSILOptimizer PRIVATE Sink.cpp SpeculativeDevirtualizer.cpp StackPromotion.cpp + StringOptimization.cpp TempLValueOpt.cpp TempRValueElimination.cpp UnsafeGuaranteedPeephole.cpp) diff --git a/lib/SILOptimizer/Transforms/CopyForwarding.cpp b/lib/SILOptimizer/Transforms/CopyForwarding.cpp index f8f0af4665d40..f930a10dc92e0 100644 --- a/lib/SILOptimizer/Transforms/CopyForwarding.cpp +++ b/lib/SILOptimizer/Transforms/CopyForwarding.cpp @@ -200,7 +200,7 @@ class AddressUserVisitor { /// Gather all instructions that use the given `address` /// -/// "Normal" uses are a whitelisted set of uses that guarantees the address is +/// "Normal" uses are a allowlisted set of uses that guarantees the address is /// only used as if it refers to a single value and all uses are accounted for /// (no address projections). /// @@ -612,8 +612,10 @@ class CopyForwarding { protected: bool propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy); - CopyAddrInst *findCopyIntoDeadTemp(CopyAddrInst *destCopy); - bool forwardDeadTempCopy(CopyAddrInst *srcCopy, CopyAddrInst *destCopy); + CopyAddrInst *findCopyIntoDeadTemp( + CopyAddrInst *destCopy, + SmallVectorImpl &debugInstsToDelete); + bool forwardDeadTempCopy(CopyAddrInst *destCopy); bool forwardPropagateCopy(); bool backwardPropagateCopy(); bool hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc); @@ -681,12 +683,10 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { // Handle copy-of-copy without analyzing uses. // Assumes that CurrentCopy->getSrc() is dead after CurrentCopy. assert(CurrentCopy->isTakeOfSrc() || hoistingDestroy); - if (auto *srcCopy = findCopyIntoDeadTemp(CurrentCopy)) { - if (forwardDeadTempCopy(srcCopy, CurrentCopy)) { - HasChanged = true; - ++NumDeadTemp; - return true; - } + if (forwardDeadTempCopy(CurrentCopy)) { + HasChanged = true; + ++NumDeadTemp; + return true; } if (forwardPropagateCopy()) { @@ -737,7 +737,9 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { /// Unlike the forward and backward propagation that finds all use points, this /// handles copies of address projections. By conservatively checking all /// intervening instructions, it avoids the need to analyze projection paths. -CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp(CopyAddrInst *destCopy) { +CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp( + CopyAddrInst *destCopy, + SmallVectorImpl &debugInstsToDelete) { auto tmpVal = destCopy->getSrc(); assert(tmpVal == CurrentDef); assert(isIdentifiedSourceValue(tmpVal)); @@ -750,8 +752,20 @@ CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp(CopyAddrInst *destCopy) { if (srcCopy->getDest() == tmpVal) return srcCopy; } + // 'SrcUserInsts' consists of all users of the 'temp' if (SrcUserInsts.count(UserInst)) return nullptr; + + // Collect all debug_value_addr instructions between temp to dest copy and + // src to temp copy. On success, these debug_value_addr instructions should + // be deleted. + if (auto *debugUser = dyn_cast(UserInst)) { + // 'SrcDebugValueInsts' consists of all the debug users of 'temp' + if (SrcDebugValueInsts.count(debugUser)) + debugInstsToDelete.push_back(debugUser); + continue; + } + if (UserInst->mayWriteToMemory()) return nullptr; } @@ -780,12 +794,16 @@ CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp(CopyAddrInst *destCopy) { /// - %temp is uninitialized following `srcCopy` and subsequent instruction /// attempts to destroy this uninitialized value. bool CopyForwarding:: -forwardDeadTempCopy(CopyAddrInst *srcCopy, CopyAddrInst *destCopy) { +forwardDeadTempCopy(CopyAddrInst *destCopy) { + SmallVector debugInstsToDelete; + auto *srcCopy = findCopyIntoDeadTemp(CurrentCopy, debugInstsToDelete); + if (!srcCopy) + return false; + LLVM_DEBUG(llvm::dbgs() << " Temp Copy:" << *srcCopy << " to " << *destCopy); assert(srcCopy->getDest() == destCopy->getSrc()); - // This pattern can be trivially folded without affecting %temp destroys: // copy_addr [...] %src, [init] %temp // copy_addr [take] %temp, [...] %dest @@ -798,6 +816,11 @@ forwardDeadTempCopy(CopyAddrInst *srcCopy, CopyAddrInst *destCopy) { .createDestroyAddr(srcCopy->getLoc(), srcCopy->getDest()); } + // Delete all dead debug_value_addr instructions + for (auto *deadDebugUser : debugInstsToDelete) { + deadDebugUser->eraseFromParent(); + } + // Either `destCopy` is a take, or the caller is hoisting a destroy: // copy_addr %temp, %dest // ... @@ -1485,10 +1508,6 @@ class CopyForwardingPass : public SILFunctionTransform if (!EnableCopyForwarding && !EnableDestroyHoisting) return; - // FIXME: We should be able to support [ossa]. - if (getFunction()->hasOwnership()) - return; - LLVM_DEBUG(llvm::dbgs() << "Copy Forwarding in Func " << getFunction()->getName() << "\n"); diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp index cad6fdd08f15a..e6e4dac4c2262 100644 --- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp @@ -60,7 +60,7 @@ class Devirtualizer : public SILFunctionTransform { void Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, ClassHierarchyAnalysis *CHA) { llvm::SmallVector NewApplies; - OptRemark::Emitter ORE(DEBUG_TYPE, F.getModule()); + OptRemark::Emitter ORE(DEBUG_TYPE, F); SmallVector Applies; for (auto &BB : F) { diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp similarity index 92% rename from lib/SILOptimizer/IPO/EagerSpecializer.cpp rename to lib/SILOptimizer/Transforms/EagerSpecializer.cpp index f647a161b1164..234b856d7c0ff 100644 --- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp @@ -738,7 +738,7 @@ SILValue EagerDispatch::emitArgumentConversion( namespace { // FIXME: This should be a function transform that pushes cloned functions on // the pass manager worklist. -class EagerSpecializerTransform : public SILModuleTransform { +class EagerSpecializerTransform : public SILFunctionTransform { public: EagerSpecializerTransform() {} @@ -751,7 +751,10 @@ class EagerSpecializerTransform : public SILModuleTransform { static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, SILFunction *GenericFunc, const SILSpecializeAttr &SA, - const ReabstractionInfo &ReInfo) { + const ReabstractionInfo &ReInfo, + SmallVectorImpl &newFunctions) { + assert(ReInfo.getSpecializedType()); + LLVM_DEBUG(llvm::dbgs() << "Specializing " << GenericFunc->getName() << "\n"); LLVM_DEBUG(auto FT = GenericFunc->getLoweredFunctionType(); @@ -767,7 +770,13 @@ static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, ReInfo.getClonerParamSubstitutionMap(), ReInfo); - SILFunction *NewFunc = FuncSpecializer.trySpecialization(); + SILFunction *NewFunc = FuncSpecializer.lookupSpecialization(); + if (!NewFunc) { + NewFunc = FuncSpecializer.tryCreateSpecialization(); + if (NewFunc) + newFunctions.push_back(NewFunc); + } + if (!NewFunc) LLVM_DEBUG(llvm::dbgs() << " Failed. Cannot specialize function.\n"); return NewFunc; @@ -779,64 +788,69 @@ void EagerSpecializerTransform::run() { return; SILOptFunctionBuilder FuncBuilder(*this); + auto &F = *getFunction(); // Process functions in any order. - for (auto &F : *getModule()) { - if (!F.shouldOptimize()) { - LLVM_DEBUG(llvm::dbgs() << " Cannot specialize function " << F.getName() - << " because it is marked to be " - "excluded from optimizations.\n"); - continue; - } - - // Only specialize functions in their home module. - if (F.isExternalDeclaration() || F.isAvailableExternally()) - continue; + if (!F.shouldOptimize()) { + LLVM_DEBUG(llvm::dbgs() << " Cannot specialize function " << F.getName() + << " because it is marked to be " + "excluded from optimizations.\n"); + return; + } - if (F.isDynamicallyReplaceable()) - continue; + // Only specialize functions in their home module. + if (F.isExternalDeclaration() || F.isAvailableExternally()) + return; - if (!F.getLoweredFunctionType()->getInvocationGenericSignature()) - continue; + if (F.isDynamicallyReplaceable()) + return; - // Create a specialized function with ReabstractionInfo for each attribute. - SmallVector SpecializedFuncs; - SmallVector ReInfoVec; - ReInfoVec.reserve(F.getSpecializeAttrs().size()); + if (!F.getLoweredFunctionType()->getInvocationGenericSignature()) + return; - // TODO: Use a decision-tree to reduce the amount of dynamic checks being - // performed. - for (auto *SA : F.getSpecializeAttrs()) { - ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), - FuncBuilder.getModule().isWholeModule(), &F, - SA->getSpecializedSignature()); - auto *NewFunc = eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back()); + // Create a specialized function with ReabstractionInfo for each attribute. + SmallVector SpecializedFuncs; + SmallVector ReInfoVec; + ReInfoVec.reserve(F.getSpecializeAttrs().size()); + SmallVector newFunctions; + + // TODO: Use a decision-tree to reduce the amount of dynamic checks being + // performed. + for (auto *SA : F.getSpecializeAttrs()) { + ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), &F, + SA->getSpecializedSignature()); + auto *NewFunc = + eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back(), newFunctions); + + SpecializedFuncs.push_back(NewFunc); + } - SpecializedFuncs.push_back(NewFunc); - } + // TODO: Optimize the dispatch code to minimize the amount + // of checks. Use decision trees for this purpose. + bool Changed = false; + for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, + [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, + const ReabstractionInfo &ReInfo) { + if (NewFunc) { + NewFunc->verify(); + Changed = true; + EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); + } + }); + + // Invalidate everything since we delete calls as well as add new + // calls and branches. + if (Changed) { + invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); + } - // TODO: Optimize the dispatch code to minimize the amount - // of checks. Use decision trees for this purpose. - bool Changed = false; - for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, - [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, - const ReabstractionInfo &ReInfo) { - if (NewFunc) { - NewFunc->verify(); - Changed = true; - EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); - } - }); - - // Invalidate everything since we delete calls as well as add new - // calls and branches. - if (Changed) { - invalidateAnalysis(&F, SILAnalysis::InvalidationKind::Everything); - } + // As specializations are created, the attributes should be removed. + F.clearSpecializeAttrs(); + F.verify(); - // As specializations are created, the attributes should be removed. - F.clearSpecializeAttrs(); - F.verify(); + for (SILFunction *newF : newFunctions) { + addFunctionToPassManagerWorklist(newF, nullptr); } } diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index cd54f6a7e5efb..e52357cd22cad 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -61,7 +61,7 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { SILOptFunctionBuilder FunctionBuilder(*this); DeadInstructionSet DeadApplies; llvm::SmallSetVector Applies; - OptRemark::Emitter ORE(DEBUG_TYPE, F.getModule()); + OptRemark::Emitter ORE(DEBUG_TYPE, F); bool Changed = false; for (auto &BB : F) { diff --git a/lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp b/lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp new file mode 100644 index 0000000000000..b20890a6b1cfd --- /dev/null +++ b/lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp @@ -0,0 +1,365 @@ +//===--- OptRemarkGenerator.cpp -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// In this pass, we define the opt-remark-generator, a simple SILVisitor that +/// attempts to infer opt-remarks for the user using heuristics. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-opt-remark-gen" + +#include "swift/AST/SemanticAttrs.h" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/OptimizationRemark.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILVisitor.h" +#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/raw_ostream.h" + +using namespace swift; + +//===----------------------------------------------------------------------===// +// Utility +//===----------------------------------------------------------------------===// + +namespace { + +struct ValueToDeclInferrer { + using Argument = OptRemark::Argument; + using ArgumentKeyKind = OptRemark::ArgumentKeyKind; + + RCIdentityFunctionInfo &rcfi; + SmallVector, 32> accessPath; + + ValueToDeclInferrer(RCIdentityFunctionInfo &rcfi) : rcfi(rcfi) {} + + /// Given a value, attempt to infer a conservative list of decls that the + /// passed in value could be referring to. This is done just using heuristics + bool infer(ArgumentKeyKind keyKind, SILValue value, + SmallVectorImpl &resultingInferredDecls); + + /// Print out a note to \p stream that beings at decl and then consumes the + /// accessPath we computed for decl producing a segmented access path, e.x.: + /// "of 'x.lhs.ivar'". + void printNote(llvm::raw_string_ostream &stream, const ValueDecl *decl); +}; + +} // anonymous namespace + +void ValueToDeclInferrer::printNote(llvm::raw_string_ostream &stream, + const ValueDecl *decl) { + stream << "of '" << decl->getBaseName(); + for (auto &pair : accessPath) { + auto baseType = pair.first; + auto &proj = pair.second; + stream << "."; + + // WARNING: This must be kept insync with isSupportedProjection! + switch (proj.getKind()) { + case ProjectionKind::Upcast: + stream << "upcast<" << proj.getCastType(baseType) << ">"; + continue; + case ProjectionKind::RefCast: + stream << "refcast<" << proj.getCastType(baseType) << ">"; + continue; + case ProjectionKind::BitwiseCast: + stream << "bitwise_cast<" << proj.getCastType(baseType) << ">"; + continue; + case ProjectionKind::Struct: + stream << proj.getVarDecl(baseType)->getBaseName(); + continue; + case ProjectionKind::Tuple: + stream << proj.getIndex(); + continue; + case ProjectionKind::Enum: + stream << proj.getEnumElementDecl(baseType)->getBaseName(); + continue; + // Object -> Address projections can never be looked through. + case ProjectionKind::Class: + case ProjectionKind::Box: + case ProjectionKind::Index: + case ProjectionKind::TailElems: + llvm_unreachable( + "Object -> Address projection should never be looked through!"); + } + + llvm_unreachable("Covered switch is not covered?!"); + } + + accessPath.clear(); + stream << "'"; +} + +// WARNING: This must be kept insync with ValueToDeclInferrer::printNote(...). +static SingleValueInstruction *isSupportedProjection(Projection p, SILValue v) { + switch (p.getKind()) { + case ProjectionKind::Upcast: + case ProjectionKind::RefCast: + case ProjectionKind::BitwiseCast: + case ProjectionKind::Struct: + case ProjectionKind::Tuple: + case ProjectionKind::Enum: + return cast(v); + // Object -> Address projections can never be looked through. + case ProjectionKind::Class: + case ProjectionKind::Box: + case ProjectionKind::Index: + case ProjectionKind::TailElems: + return nullptr; + } + llvm_unreachable("Covered switch is not covered?!"); +} + +bool ValueToDeclInferrer::infer( + ArgumentKeyKind keyKind, SILValue value, + SmallVectorImpl &resultingInferredDecls) { + // This is a linear IR traversal using a 'falling while loop'. That means + // every time through the loop we are trying to handle a case before we hit + // the bottom of the while loop where we always return true (since we did not + // hit a could not compute case). Reassign value and continue to go to the + // next step. + while (true) { + // First check for "identified values" like arguments and global_addr. + if (auto *arg = dyn_cast(value)) + if (auto *decl = arg->getDecl()) { + std::string msg; + { + llvm::raw_string_ostream stream(msg); + printNote(stream, decl); + } + resultingInferredDecls.push_back( + Argument({keyKind, "InferredValue"}, std::move(msg), decl)); + return true; + } + + if (auto *ga = dyn_cast(value)) + if (auto *decl = ga->getReferencedGlobal()->getDecl()) { + std::string msg; + { + llvm::raw_string_ostream stream(msg); + printNote(stream, decl); + } + resultingInferredDecls.push_back( + Argument({keyKind, "InferredValue"}, std::move(msg), decl)); + return true; + } + + // Then visit our users and see if we can find a debug_value that provides + // us with a decl we can use to construct an argument. + bool foundDeclFromUse = false; + for (auto *use : value->getUses()) { + // Skip type dependent uses. + if (use->isTypeDependent()) + continue; + + if (auto *dvi = dyn_cast(use->getUser())) { + if (auto *decl = dvi->getDecl()) { + std::string msg; + { + llvm::raw_string_ostream stream(msg); + printNote(stream, decl); + } + resultingInferredDecls.push_back( + Argument({keyKind, "InferredValue"}, std::move(msg), decl)); + foundDeclFromUse = true; + } + } + } + if (foundDeclFromUse) + return true; + + // At this point, we could not infer any argument. See if we can look + // through loads. + // + // TODO: Add GEPs to construct a ProjectionPath. + + // Finally, see if we can look through a load... + if (auto *li = dyn_cast(value)) { + value = stripAccessMarkers(li->getOperand()); + continue; + } + + if (auto proj = Projection(value)) { + if (auto *projInst = isSupportedProjection(proj, value)) { + value = projInst->getOperand(0); + accessPath.emplace_back(value->getType(), proj); + continue; + } + } + + // If we reached this point, we finished falling through the loop and return + // true. + return true; + } +} + +//===----------------------------------------------------------------------===// +// Opt Remark Generator Visitor +//===----------------------------------------------------------------------===// + +namespace { + +struct OptRemarkGeneratorInstructionVisitor + : public SILInstructionVisitor { + SILModule &mod; + OptRemark::Emitter ORE; + + /// A class that we use to infer the decl that is associated with a + /// miscellaneous SIL value. This is just a heuristic that is to taste. + ValueToDeclInferrer valueToDeclInferrer; + + OptRemarkGeneratorInstructionVisitor(SILFunction &fn, + RCIdentityFunctionInfo &rcfi) + : mod(fn.getModule()), ORE(DEBUG_TYPE, fn), valueToDeclInferrer(rcfi) {} + + void visitStrongRetainInst(StrongRetainInst *sri); + void visitStrongReleaseInst(StrongReleaseInst *sri); + void visitRetainValueInst(RetainValueInst *rvi); + void visitReleaseValueInst(ReleaseValueInst *rvi); + void visitSILInstruction(SILInstruction *) {} +}; + +} // anonymous namespace + +void OptRemarkGeneratorInstructionVisitor::visitStrongRetainInst( + StrongRetainInst *sri) { + ORE.emit([&]() { + using namespace OptRemark; + SmallVector inferredArgs; + bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, + sri->getOperand(), inferredArgs); + (void)foundArgs; + + // Retains begin a lifetime scope so we infer scan forward. + auto remark = RemarkMissed("memory", *sri, + SourceLocInferenceBehavior::ForwardScanOnly) + << "retain of type '" + << NV("ValueType", sri->getOperand()->getType()) << "'"; + for (auto arg : inferredArgs) { + remark << arg; + } + return remark; + }); +} + +void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst( + StrongReleaseInst *sri) { + ORE.emit([&]() { + using namespace OptRemark; + // Releases end a lifetime scope so we infer scan backward. + SmallVector inferredArgs; + bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, + sri->getOperand(), inferredArgs); + (void)foundArgs; + + auto remark = RemarkMissed("memory", *sri, + SourceLocInferenceBehavior::BackwardScanOnly) + << "release of type '" + << NV("ValueType", sri->getOperand()->getType()) << "'"; + for (auto arg : inferredArgs) { + remark << arg; + } + return remark; + }); +} + +void OptRemarkGeneratorInstructionVisitor::visitRetainValueInst( + RetainValueInst *rvi) { + ORE.emit([&]() { + using namespace OptRemark; + SmallVector inferredArgs; + bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, + rvi->getOperand(), inferredArgs); + (void)foundArgs; + // Retains begin a lifetime scope, so we infer scan forwards. + auto remark = RemarkMissed("memory", *rvi, + SourceLocInferenceBehavior::ForwardScanOnly) + << "retain of type '" + << NV("ValueType", rvi->getOperand()->getType()) << "'"; + for (auto arg : inferredArgs) { + remark << arg; + } + return remark; + }); +} + +void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst( + ReleaseValueInst *rvi) { + ORE.emit([&]() { + using namespace OptRemark; + SmallVector inferredArgs; + bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, + rvi->getOperand(), inferredArgs); + (void)foundArgs; + + // Releases end a lifetime scope so we infer scan backward. + auto remark = RemarkMissed("memory", *rvi, + SourceLocInferenceBehavior::BackwardScanOnly) + << "release of type '" + << NV("ValueType", rvi->getOperand()->getType()) << "'"; + for (auto arg : inferredArgs) { + remark << arg; + } + return remark; + }); +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoint +//===----------------------------------------------------------------------===// + +namespace { + +class OptRemarkGenerator : public SILFunctionTransform { + ~OptRemarkGenerator() override {} + + bool isOptRemarksEnabled() { + auto *fn = getFunction(); + // TODO: Put this on LangOpts as a helper. + auto &langOpts = fn->getASTContext().LangOpts; + + return bool(langOpts.OptimizationRemarkMissedPattern) || + bool(langOpts.OptimizationRemarkPassedPattern) || + fn->getModule().getSILRemarkStreamer() || + fn->hasSemanticsAttrThatStartsWith( + semantics::FORCE_EMIT_OPT_REMARK_PREFIX); + } + + /// The entry point to the transformation. + void run() override { + if (!isOptRemarksEnabled()) + return; + + auto *fn = getFunction(); + LLVM_DEBUG(llvm::dbgs() << "Visiting: " << fn->getName() << "\n"); + auto &rcfi = *getAnalysis()->get(fn); + OptRemarkGeneratorInstructionVisitor visitor(*fn, rcfi); + for (auto &block : *fn) { + for (auto &inst : block) { + visitor.visit(&inst); + } + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createOptRemarkGenerator() { + return new OptRemarkGenerator(); +} diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index fbf76e306f85d..e052a6b000f3c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -295,11 +295,12 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { Results.push_back(SILResultInfo( switchInfo.Br->getArg(0)->getType().getASTType(), ResultConvention::Owned)); - auto ExtInfo = - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, /*noescape*/ false, - DifferentiabilityKind::NonDifferentiable, - /*clangFunctionType*/ nullptr); + auto ExtInfo = SILFunctionType::ExtInfoBuilder( + SILFunctionType::Representation::Thin, + /*pseudogeneric*/ false, /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr) + .build(); auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, @@ -1176,12 +1177,13 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { ++OrigSigIdx; } - auto ExtInfo = SILFunctionType::ExtInfo( - SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false, - DifferentiabilityKind::NonDifferentiable, - /*clangFunctionType*/ nullptr); + auto ExtInfo = + SILFunctionType::ExtInfoBuilder(SILFunctionType::Representation::Thin, + /*pseudogeneric*/ false, + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr) + .build(); SmallVector Results; // If we don't have a bridged return we changed from @autoreleased to @owned diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index f018f3a695b58..e07553e784ef4 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -298,6 +298,13 @@ bool SILPerformanceInliner::isProfitableToInline( // It is always OK to inline a simple call. // TODO: May be consider also the size of the callee? if (isPureCall(AI, SEA)) { + OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { + using namespace OptRemark; + return RemarkPassed("Inline", *AI.getInstruction()) + << "Pure call. Always profitable to inline " + << NV("Callee", Callee); + }); + LLVM_DEBUG(dumpCaller(AI.getFunction()); llvm::dbgs() << " pure-call decision " << Callee->getName() << '\n'); @@ -432,11 +439,8 @@ bool SILPerformanceInliner::isProfitableToInline( // The access is dynamic and has no nested conflict // See if the storage location is considered by // access enforcement optimizations - AccessedStorage storage = - findAccessedStorageNonNested(BAI->getSource()); - if (BAI->hasNoNestedConflict() && - (storage.isUniquelyIdentified() || - storage.getKind() == AccessedStorage::Class)) { + AccessedStorage storage = findAccessedStorage(BAI->getSource()); + if (BAI->hasNoNestedConflict() && (storage.isFormalAccessBase())) { BlockW.updateBenefit(ExclusivityBenefitWeight, ExclusivityBenefitBase); } else { @@ -490,9 +494,24 @@ bool SILPerformanceInliner::isProfitableToInline( auto *bb = AI.getInstruction()->getParent(); auto bbIt = BBToWeightMap.find(bb); if (bbIt != BBToWeightMap.end()) { - return profileBasedDecision(AI, Benefit, Callee, CalleeCost, - NumCallerBlocks, bbIt); + if (profileBasedDecision(AI, Benefit, Callee, CalleeCost, NumCallerBlocks, + bbIt)) { + OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { + using namespace OptRemark; + return RemarkPassed("Inline", *AI.getInstruction()) + << "Profitable due to provided profile"; + }); + return true; + } + + OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { + using namespace OptRemark; + return RemarkMissed("Inline", *AI.getInstruction()) + << "Not profitable due to provided profile"; + }); + return false; } + if (isClassMethodAtOsize && Benefit > OSizeClassMethodBenefit) { Benefit = OSizeClassMethodBenefit; } @@ -501,7 +520,7 @@ bool SILPerformanceInliner::isProfitableToInline( if (CalleeCost > Benefit) { OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { using namespace OptRemark; - return RemarkMissed("NoInlinedCost", *AI.getInstruction()) + return RemarkMissed("Inline", *AI.getInstruction()) << "Not profitable to inline function " << NV("Callee", Callee) << " (cost = " << NV("Cost", CalleeCost) << ", benefit = " << NV("Benefit", Benefit) << ")"; @@ -1011,7 +1030,7 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { DominanceAnalysis *DA = PM->getAnalysis(); SILLoopAnalysis *LA = PM->getAnalysis(); SideEffectAnalysis *SEA = PM->getAnalysis(); - OptRemark::Emitter ORE(DEBUG_TYPE, getFunction()->getModule()); + OptRemark::Emitter ORE(DEBUG_TYPE, *getFunction()); if (getOptions().InlineThreshold == 0) { return; diff --git a/lib/SILOptimizer/Transforms/PruneVTables.cpp b/lib/SILOptimizer/Transforms/PruneVTables.cpp index c918e88542e69..18543900f921e 100644 --- a/lib/SILOptimizer/Transforms/PruneVTables.cpp +++ b/lib/SILOptimizer/Transforms/PruneVTables.cpp @@ -20,18 +20,27 @@ #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +STATISTIC(NumNonoverriddenVTableEntries, + "# of vtable entries marked non-overridden"); + using namespace swift; namespace { class PruneVTables : public SILModuleTransform { void runOnVTable(SILModule *M, SILVTable *vtable) { + LLVM_DEBUG(llvm::dbgs() << "PruneVTables inspecting table:\n"; + vtable->print(llvm::dbgs())); for (auto &entry : vtable->getMutableEntries()) { // We don't need to worry about entries that are overridden, // or have already been found to have no overrides. - if (entry.isNonOverridden()) + if (entry.isNonOverridden()) { + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " is already nonoverridden\n"); continue; + } switch (entry.getKind()) { case SILVTable::Entry::Normal: @@ -39,29 +48,52 @@ class PruneVTables : public SILModuleTransform { break; case SILVTable::Entry::Override: + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " is an override\n"); continue; } // The destructor entry must remain. if (entry.getMethod().kind == SILDeclRef::Kind::Deallocator) { + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " is a destructor\n"); continue; } auto methodDecl = entry.getMethod().getAbstractFunctionDecl(); - if (!methodDecl) + if (!methodDecl) { + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " is not a function decl\n"); continue; + } // Is the method declared final? if (!methodDecl->isFinal()) { // Are callees of this entry statically knowable? - if (!calleesAreStaticallyKnowable(*M, entry.getMethod())) + if (!calleesAreStaticallyKnowable(*M, entry.getMethod())) { + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " does not have statically-knowable callees\n"); continue; + } // Does the method have any overrides in this module? - if (methodDecl->isOverridden()) + if (methodDecl->isOverridden()) { + LLVM_DEBUG(llvm::dbgs() << "-- entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " has overrides\n"); continue; + } } + LLVM_DEBUG(llvm::dbgs() << "++ entry for "; + entry.getMethod().print(llvm::dbgs()); + llvm::dbgs() << " can be marked non-overridden!\n"); + ++NumNonoverriddenVTableEntries; entry.setNonOverridden(true); + vtable->updateVTableCache(entry); } } diff --git a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp index 331b44b0fc329..0a40cb0c05d46 100644 --- a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp +++ b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp @@ -28,6 +28,7 @@ #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; @@ -35,6 +36,23 @@ using namespace swift::Lowering; STATISTIC(NumExpand, "Number of instructions expanded"); +static llvm::cl::opt EnableExpandAll("sil-lower-agg-instrs-expand-all", + llvm::cl::init(false)); + +//===----------------------------------------------------------------------===// +// Utility +//===----------------------------------------------------------------------===// + +/// We only expand if we are not in ownership and shouldExpand is true. The +/// reason why is that this was originally done to help the low level ARC +/// optimizer. To the high level ARC optimizer, this is just noise and +/// unnecessary IR. At the same time for testing purposes, we want to provide a +/// way even with ownership enabled to expand so we can check correctness. +static bool shouldExpandShim(SILFunction *fn, SILType type) { + return EnableExpandAll || + (!fn->hasOwnership() && shouldExpand(fn->getModule(), type)); +} + //===----------------------------------------------------------------------===// // Higher Level Operation Expansion //===----------------------------------------------------------------------===// @@ -43,26 +61,37 @@ STATISTIC(NumExpand, "Number of instructions expanded"); /// non-address only type. We do this here so we can process the resulting /// loads/stores. /// -/// This peephole implements the following optimizations: +/// This peephole implements the following optimizations with the ossa version +/// of the optimization first. /// /// copy_addr %0 to %1 : $*T /// -> +/// %new = load [copy] %0 : $*T +/// store %new to [assign] %1 : $*T +/// -> /// %new = load %0 : $*T // Load the new value from the source -/// %old = load %1 : $*T // Load the old value from the destination /// strong_retain %new : $T // Retain the new value +/// %old = load %1 : $*T // Load the old value from the destination /// strong_release %old : $T // Release the old /// store %new to %1 : $*T // Store the new value to the destination /// /// copy_addr [take] %0 to %1 : $*T /// -> +/// // load [take], not load [copy]! +/// %new = load [take] %0 : $*T +/// store %new to [assign] %1 : $*T +/// -> /// %new = load %0 : $*T -/// %old = load %1 : $*T /// // no retain of %new! +/// %old = load %1 : $*T /// strong_release %old : $T /// store %new to %1 : $*T /// /// copy_addr %0 to [initialization] %1 : $*T /// -> +/// %new = load [copy] %0 : $*T +/// store %new to [init] %1 : $*T +/// -> /// %new = load %0 : $*T /// strong_retain %new : $T /// // no load/release of %old! @@ -70,13 +99,15 @@ STATISTIC(NumExpand, "Number of instructions expanded"); /// /// copy_addr [take] %0 to [initialization] %1 : $*T /// -> +/// %new = load [take] %0 : $*T +/// store %new to [init] %1 : $*T +/// -> /// %new = load %0 : $*T /// // no retain of %new! /// // no load/release of %old! /// store %new to %1 : $*T static bool expandCopyAddr(CopyAddrInst *cai) { SILFunction *fn = cai->getFunction(); - SILModule &module = cai->getModule(); SILValue source = cai->getSrc(); // If we have an address only type don't do anything. @@ -84,57 +115,49 @@ static bool expandCopyAddr(CopyAddrInst *cai) { if (srcType.isAddressOnly(*fn)) return false; - bool expand = shouldExpand(module, srcType.getObjectType()); + bool expand = shouldExpandShim(fn, srcType.getObjectType()); using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind; auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents : TypeExpansionKind::None; SILBuilderWithScope builder(cai); - // %new = load %0 : $*T - LoadInst *newValue = builder.createLoad(cai->getLoc(), source, - LoadOwnershipQualifier::Unqualified); + // If our object type is not trivial, we may need to destroy the old value and + // copy the new one. Handle the trivial case quickly and return. + if (srcType.isTrivial(*fn)) { + SILValue newValue = builder.emitLoadValueOperation( + cai->getLoc(), source, LoadOwnershipQualifier::Trivial); + SILValue destAddr = cai->getDest(); + // Create the store. + builder.emitStoreValueOperation(cai->getLoc(), newValue, destAddr, + StoreOwnershipQualifier::Trivial); + ++NumExpand; + return true; + } + // %new = load [copy|take] %0 : $*T + auto loadQual = [&]() -> LoadOwnershipQualifier { + if (IsTake_t::IsTake == cai->isTakeOfSrc()) + return LoadOwnershipQualifier::Take; + return LoadOwnershipQualifier::Copy; + }(); + SILValue newValue = builder.emitLoweredLoadValueOperation( + cai->getLoc(), source, loadQual, expansionKind); SILValue destAddr = cai->getDest(); - // If our object type is not trivial, we may need to release the old value and - // retain the new one. - - auto &typeLowering = fn->getTypeLowering(srcType); - - // If we have a non-trivial type... - if (!typeLowering.isTrivial()) { - // If we are not initializing: - // %old = load %1 : $*T - auto isInit = cai->isInitializationOfDest(); - LoadInst *oldValue = nullptr; - if (IsInitialization_t::IsNotInitialization == isInit) { - oldValue = builder.createLoad(cai->getLoc(), destAddr, - LoadOwnershipQualifier::Unqualified); - } - - // If we are not taking and have a reference type: - // strong_retain %new : $*T - // or if we have a non-trivial non-reference type. - // retain_value %new : $*T - if (IsTake_t::IsNotTake == cai->isTakeOfSrc()) { - typeLowering.emitLoweredCopyValue(builder, cai->getLoc(), newValue, - expansionKind); - } - - // If we are not initializing: - // strong_release %old : $*T - // *or* - // release_value %old : $*T - if (oldValue) { - typeLowering.emitLoweredDestroyValue(builder, cai->getLoc(), oldValue, - expansionKind); - } - } - - // Create the store. - builder.createStore(cai->getLoc(), newValue, destAddr, - StoreOwnershipQualifier::Unqualified); + // Create the store in the guaranteed uninitialized memory. + // + // store %new to [init|assign] %1 + // + // If we are not initializing the destination, we need to destroy what is + // currently there before we re-initialize the memory. + auto storeQualifier = [&]() -> StoreOwnershipQualifier { + if (IsInitialization_t::IsInitialization != cai->isInitializationOfDest()) + return StoreOwnershipQualifier::Assign; + return StoreOwnershipQualifier::Init; + }(); + builder.emitLoweredStoreValueOperation(cai->getLoc(), newValue, destAddr, + storeQualifier, expansionKind); ++NumExpand; return true; @@ -142,7 +165,6 @@ static bool expandCopyAddr(CopyAddrInst *cai) { static bool expandDestroyAddr(DestroyAddrInst *dai) { SILFunction *fn = dai->getFunction(); - SILModule &module = dai->getModule(); SILBuilderWithScope builder(dai); // Strength reduce destroy_addr inst into release/store if @@ -154,13 +176,16 @@ static bool expandDestroyAddr(DestroyAddrInst *dai) { if (type.isAddressOnly(*fn)) return false; - bool expand = shouldExpand(module, type.getObjectType()); + // We only expand if ownership is not enabled and we do not have a large + // type. This was something that was only really beneficial for the low level + // ARC optimizer which runs without ownership enabled. + bool expand = shouldExpandShim(fn, type.getObjectType()); // If we have a non-trivial type... if (!type.isTrivial(*fn)) { - // If we have a type with reference semantics, emit a load/strong release. - LoadInst *li = builder.createLoad(dai->getLoc(), addr, - LoadOwnershipQualifier::Unqualified); + // If we have a type with reference semantics, emit a load/destroy. + SILValue li = builder.emitLoadValueOperation(dai->getLoc(), addr, + LoadOwnershipQualifier::Take); auto &typeLowering = fn->getTypeLowering(type); using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind; auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents @@ -175,7 +200,6 @@ static bool expandDestroyAddr(DestroyAddrInst *dai) { static bool expandReleaseValue(ReleaseValueInst *rvi) { SILFunction *fn = rvi->getFunction(); - SILModule &module = rvi->getModule(); SILBuilderWithScope builder(rvi); // Strength reduce destroy_addr inst into release/store if @@ -184,11 +208,11 @@ static bool expandReleaseValue(ReleaseValueInst *rvi) { // If we have an address only type, do nothing. SILType type = value->getType(); - assert(!SILModuleConventions(module).useLoweredAddresses() || + assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() || type.isLoadable(*fn) && "release_value should never be called on a non-loadable type."); - if (!shouldExpand(module, type.getObjectType())) + if (!shouldExpandShim(fn, type.getObjectType())) return false; auto &TL = fn->getTypeLowering(type); @@ -203,7 +227,6 @@ static bool expandReleaseValue(ReleaseValueInst *rvi) { static bool expandRetainValue(RetainValueInst *rvi) { SILFunction *fn = rvi->getFunction(); - SILModule &module = rvi->getModule(); SILBuilderWithScope builder(rvi); // Strength reduce destroy_addr inst into release/store if @@ -212,11 +235,11 @@ static bool expandRetainValue(RetainValueInst *rvi) { // If we have an address only type, do nothing. SILType type = value->getType(); - assert(!SILModuleConventions(module).useLoweredAddresses() || + assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() || type.isLoadable(*fn) && "Copy Value can only be called on loadable types."); - if (!shouldExpand(module, type.getObjectType())) + if (!shouldExpandShim(fn, type.getObjectType())) return false; auto &typeLowering = fn->getTypeLowering(type); @@ -291,9 +314,6 @@ class SILLowerAggregate : public SILFunctionTransform { /// The entry point to the transformation. void run() override { SILFunction *f = getFunction(); - // FIXME: Can we support ownership? - if (f->hasOwnership()) - return; LLVM_DEBUG(llvm::dbgs() << "***** LowerAggregate on function: " << f->getName() << " *****\n"); bool changed = processFunction(*f); diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index f1d224c485d32..332af38b3f2b3 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -375,15 +375,15 @@ static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { static void replaceDestroy(DestroyAddrInst *DAI, SILValue NewValue) { SILFunction *F = DAI->getFunction(); + auto Ty = DAI->getOperand()->getType(); - assert(DAI->getOperand()->getType().isLoadable(*F) && + assert(Ty.isLoadable(*F) && "Unexpected promotion of address-only type!"); - assert(NewValue && "Expected a value to release!"); + assert(NewValue || (Ty.is() && Ty.getAs()->getNumElements() == 0)); SILBuilderWithScope Builder(DAI); - auto Ty = DAI->getOperand()->getType(); auto &TL = F->getTypeLowering(Ty); bool expand = shouldExpand(DAI->getModule(), diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index fbcc7ccca62c0..b931a3c156769 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -67,8 +67,6 @@ class SROAMemoryUseAnalyzer { private: SILValue createAgg(SILBuilder &B, SILLocation Loc, SILType Ty, ArrayRef Elements); - SILValue createAggProjection(SILBuilder &B, SILLocation Loc, - SILValue Operand, unsigned EltNo); unsigned getEltNoForProjection(SILInstruction *Inst); void createAllocas(llvm::SmallVector &NewAllocations); }; @@ -87,24 +85,6 @@ SROAMemoryUseAnalyzer::createAgg(SILBuilder &B, SILLocation Loc, return B.createStruct(Loc, Ty, Elements); } -SILValue -SROAMemoryUseAnalyzer::createAggProjection(SILBuilder &B, SILLocation Loc, - SILValue Operand, - unsigned EltNo) { - if (TT) - return B.createTupleExtract(Loc, Operand, EltNo); - - assert(SD && "SD should not be null since either it or TT must be set at " - "this point."); - - auto Properties = SD->getStoredProperties(); - unsigned Counter = 0; - for (auto *D : Properties) - if (Counter++ == EltNo) - return B.createStructExtract(Loc, Operand, D); - llvm_unreachable("Unknown field."); -} - unsigned SROAMemoryUseAnalyzer::getEltNoForProjection(SILInstruction *Inst) { if (TT) return cast(Inst)->getFieldNo(); @@ -249,9 +229,10 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector &Worklist for (auto *LI : Loads) { SILBuilderWithScope B(LI); llvm::SmallVector Elements; - for (auto *NewAI : NewAllocations) - Elements.push_back(B.createLoad(LI->getLoc(), NewAI, - LoadOwnershipQualifier::Unqualified)); + for (auto *NewAI : NewAllocations) { + Elements.push_back(B.emitLoadValueOperation(LI->getLoc(), NewAI, + LI->getOwnershipQualifier())); + } SILValue Agg = createAgg(B, LI->getLoc(), LI->getType().getObjectType(), Elements); LI->replaceAllUsesWith(Agg); @@ -260,12 +241,15 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector &Worklist // Change any aggregate stores into extracts + field stores. for (auto *SI : Stores) { - SILBuilderWithScope B(SI); - for (unsigned EltNo : indices(NewAllocations)) - B.createStore(SI->getLoc(), - createAggProjection(B, SI->getLoc(), SI->getSrc(), EltNo), - NewAllocations[EltNo], - StoreOwnershipQualifier::Unqualified); + SILBuilderWithScope builder(SI); + SmallVector destructured; + builder.emitDestructureValueOperation(SI->getLoc(), SI->getSrc(), + destructured); + for (unsigned eltNo : indices(NewAllocations)) { + builder.emitStoreValueOperation(SI->getLoc(), destructured[eltNo], + NewAllocations[eltNo], + StoreOwnershipQualifier::Init); + } SI->eraseFromParent(); } @@ -301,18 +285,33 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector &Worklist eraseFromParentWithDebugInsts(AI); } -static bool runSROAOnFunction(SILFunction &Fn) { +/// Returns true, if values of \ty should be ignored, because \p ty is known +/// by a high-level SIL optimization. Values of that type must not be split +/// so that those high-level optimizations can analyze the code. +static bool isSemanticType(ASTContext &ctxt, SILType ty) { + NominalTypeDecl *stringDecl = ctxt.getStringDecl(); + if (ty.getStructOrBoundGenericStruct() == stringDecl) + return true; + return false; +} + +static bool runSROAOnFunction(SILFunction &Fn, bool splitSemanticTypes) { std::vector Worklist; bool Changed = false; + ASTContext &ctxt = Fn.getModule().getASTContext(); // For each basic block BB in Fn... for (auto &BB : Fn) // For each instruction in BB... for (auto &I : BB) // If the instruction is an alloc stack inst, add it to the worklist. - if (auto *AI = dyn_cast(&I)) + if (auto *AI = dyn_cast(&I)) { + if (!splitSemanticTypes && isSemanticType(ctxt, AI->getElementType())) + continue; + if (shouldExpand(Fn.getModule(), AI->getElementType())) Worklist.push_back(AI); + } while (!Worklist.empty()) { AllocStackInst *AI = Worklist.back(); @@ -332,18 +331,19 @@ static bool runSROAOnFunction(SILFunction &Fn) { namespace { class SILSROA : public SILFunctionTransform { + bool splitSemanticTypes; + +public: + SILSROA(bool splitSemanticTypes) : splitSemanticTypes(splitSemanticTypes) { } + /// The entry point to the transformation. void run() override { SILFunction *F = getFunction(); - // FIXME: We should be able to handle ownership. - if (F->hasOwnership()) - return; - LLVM_DEBUG(llvm::dbgs() << "***** SROA on function: " << F->getName() << " *****\n"); - if (runSROAOnFunction(*F)) + if (runSROAOnFunction(*F, splitSemanticTypes)) invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } @@ -352,5 +352,9 @@ class SILSROA : public SILFunctionTransform { SILTransform *swift::createSROA() { - return new SILSROA(); + return new SILSROA(/*splitSemanticTypes*/ true); +} + +SILTransform *swift::createEarlySROA() { + return new SILSROA(/*splitSemanticTypes*/ false); } diff --git a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp b/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp index a11f9d6de8653..45a05e6c0db3c 100644 --- a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp @@ -2090,11 +2090,16 @@ class StorageGuaranteesLoadVisitor void visitNonAccess(SILValue addr) { return answer(true); } - - void visitIncomplete(SILValue projectedAddr, SILValue parentAddr) { - return next(parentAddr); + + void visitCast(SingleValueInstruction *cast, Operand *parentAddr) { + return next(parentAddr->get()); } - + + void visitPathComponent(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + return next(parentAddr->get()); + } + void visitPhi(SILPhiArgument *phi) { // We shouldn't have address phis in OSSA SIL, so we don't need to recur // through the predecessors here. diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index 120cab28900ff..325c9bd7a7daf 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -616,7 +616,7 @@ namespace { } } - OptRemark::Emitter ORE(DEBUG_TYPE, CurFn.getModule()); + OptRemark::Emitter ORE(DEBUG_TYPE, CurFn); // Go over the collected calls and try to insert speculative calls. for (auto AI : ToSpecialize) Changed |= tryToSpeculateTarget(AI, CHA, ORE); diff --git a/lib/SILOptimizer/Transforms/StringOptimization.cpp b/lib/SILOptimizer/Transforms/StringOptimization.cpp new file mode 100644 index 0000000000000..aa737a26497b5 --- /dev/null +++ b/lib/SILOptimizer/Transforms/StringOptimization.cpp @@ -0,0 +1,644 @@ +//===--- StringOptimization.cpp - Optimize string operations --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 pass performs several optimizations on String operations. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "string-optimization" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Analysis/ValueTracking.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILGlobalVariable.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/AST/SemanticAttrs.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ASTMangler.h" +#include "swift/Demangling/Demangle.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Optimizes String operations with constant operands. + +/// Specifically: +/// * Replaces x.append(y) with x = y if x is empty. +/// * Removes x.append("") +/// * Replaces x.append(y) with x = x + y if x and y are constant strings. +/// * Replaces _typeName(T.self) with a constant string if T is statically +/// known. +/// +/// This pass must run on high-level SIL, where semantic calls are still in +/// place. +/// +/// The optimization is implemented in a simple way. Therfore it cannot handle +/// complicated patterns, e.g. the dataflow analysis for the String.append self +/// argument is only done within a single block. +/// But this is totally sufficient to be able to constant propagate strings in +/// string interpolations. +/// +/// If we want to make this optimization more powerful it's best done by using +/// the ConstExprStepEvaluator (which is currently lacking a few features to be +/// used for this optimization). +class StringOptimization { + + struct StringInfo { + /// The string + StringRef str; + + /// Negative means: not constant + int reservedCapacity = 0; + + StringInfo(StringRef str, int reservedCapacity = 0) : + str(str), reservedCapacity(reservedCapacity) { } + + bool isConstant() const { return reservedCapacity >= 0; } + bool isEmpty() const { return isConstant() && str.empty(); } + + static StringInfo unknown() { return StringInfo(StringRef(), -1); } + }; + + /// The stdlib's String type. + SILType stringType; + + /// The String initializer which takes an UTF8 string literal as argument. + SILFunction *makeUTF8Func = nullptr; + + /// Caches the analysis result for an alloc_stack or an inout function + /// argument, whether it is an "identifyable" object. + /// See mayWriteToIdentifyableObject(). + llvm::DenseMap identifyableObjectsCache; + +public: + bool run(SILFunction *F); + +private: + + bool optimizeBlock(SILBasicBlock &block); + + bool optimizeStringAppend(ApplyInst *appendCall, + llvm::DenseMap &storedStrings); + bool optimizeStringConcat(ApplyInst *concatCall); + bool optimizeTypeName(ApplyInst *typeNameCall); + + static ApplyInst *isSemanticCall(SILInstruction *inst, StringRef attr, + unsigned numArgs); + StoreInst *isStringStoreToIdentifyableObject(SILInstruction *inst); + static void invalidateModifiedObjects(SILInstruction *inst, + llvm::DenseMap &storedStrings); + static StringInfo getStringInfo(SILValue value); + static StringInfo getStringFromStaticLet(SILValue value); + + static Optional getIntConstant(SILValue value); + static void replaceAppendWith(ApplyInst *appendCall, SILValue newValue); + static SILValue copyValue(SILValue value, SILInstruction *before); + ApplyInst *createStringInit(StringRef str, SILInstruction *beforeInst); +}; + +/// The main entry point of the optimization. +bool StringOptimization::run(SILFunction *F) { + NominalTypeDecl *stringDecl = F->getModule().getASTContext().getStringDecl(); + if (!stringDecl) + return false; + stringType = SILType::getPrimitiveObjectType( + stringDecl->getDeclaredInterfaceType()->getCanonicalType()); + + bool changed = false; + + for (SILBasicBlock &block : *F) { + changed |= optimizeBlock(block); + } + return changed; +} + +/// Run the optimization on a basic block. +bool StringOptimization::optimizeBlock(SILBasicBlock &block) { + bool changed = false; + + /// Maps identifyable objects (alloc_stack, inout parameters) to string values + /// which are stored in those objects. + llvm::DenseMap storedStrings; + + for (auto iter = block.begin(); iter != block.end();) { + SILInstruction *inst = &*iter++; + + if (StoreInst *store = isStringStoreToIdentifyableObject(inst)) { + storedStrings[store->getDest()] = store->getSrc(); + continue; + } + if (ApplyInst *append = isSemanticCall(inst, semantics::STRING_APPEND, 2)) { + if (optimizeStringAppend(append, storedStrings)) { + changed = true; + continue; + } + } + if (ApplyInst *append = isSemanticCall(inst, semantics::STRING_CONCAT, 3)) { + if (optimizeStringConcat(append)) { + changed = true; + continue; + } + } + if (ApplyInst *typeName = isSemanticCall(inst, semantics::TYPENAME, 2)) { + if (optimizeTypeName(typeName)) { + changed = true; + continue; + } + } + // Remove items from storedStrings if inst overwrites (or potentially + // overwrites) a stored String in an identifyable object. + invalidateModifiedObjects(inst, storedStrings); + } + return changed; +} + +/// Optimize String.append in case anything is known about the parameters. +bool StringOptimization::optimizeStringAppend(ApplyInst *appendCall, + llvm::DenseMap &storedStrings) { + SILValue rhs = appendCall->getArgument(0); + StringInfo rhsString = getStringInfo(rhs); + + // Remove lhs.append(rhs) if rhs is empty. + if (rhsString.isEmpty()) { + appendCall->eraseFromParent(); + return true; + } + + SILValue lhsAddr = appendCall->getArgument(1); + StringInfo lhsString = getStringInfo(storedStrings[lhsAddr]); + + // The following two optimizations are a trade-off: Performance-wise it may be + // benefitial to initialize an empty string with reserved capacity and then + // append multiple other string components. + // Removing the empty string (with the reserved capacity) might result in more + // allocations. + // So we just do this optimization up to a certain capacity limit (found by + // experiment). + if (lhsString.reservedCapacity > 50) + return false; + + // Replace lhs.append(rhs) with 'lhs = rhs' if lhs is empty. + if (lhsString.isEmpty()) { + replaceAppendWith(appendCall, copyValue(rhs, appendCall)); + storedStrings[lhsAddr] = rhs; + return true; + } + + // Replace lhs.append(rhs) with "lhs = lhs + rhs" if both lhs and rhs are + // constant. + if (lhsString.isConstant() && rhsString.isConstant()) { + std::string concat = lhsString.str; + concat += rhsString.str; + if (ApplyInst *stringInit = createStringInit(concat, appendCall)) { + replaceAppendWith(appendCall, stringInit); + storedStrings[lhsAddr] = stringInit; + return true; + } + } + + return false; +} + +/// Optimize String.+ in case anything is known about the parameters. +bool StringOptimization::optimizeStringConcat(ApplyInst *concatCall) { + SILValue lhs = concatCall->getArgument(0); + SILValue rhs = concatCall->getArgument(1); + StringInfo rhsString = getStringInfo(rhs); + + // Replace lhs + "" with lhs + if (rhsString.isEmpty()) { + lhs = copyValue(lhs, concatCall); + concatCall->replaceAllUsesWith(lhs); + concatCall->eraseFromParent(); + return true; + } + + // Replace "" + rhs with rhs + StringInfo lhsString = getStringInfo(lhs); + if (lhsString.isEmpty()) { + rhs = copyValue(rhs, concatCall); + concatCall->replaceAllUsesWith(rhs); + concatCall->eraseFromParent(); + return true; + } + + // Replace lhs + rhs with "lhs + rhs" if both lhs and rhs are constant. + if (lhsString.isConstant() && rhsString.isConstant()) { + std::string concat = lhsString.str; + concat += rhsString.str; + if (ApplyInst *stringInit = createStringInit(concat, concatCall)) { + concatCall->replaceAllUsesWith(stringInit); + concatCall->eraseFromParent(); + return true; + } + } + + return false; +} + +/// Checks if the demangling tree contains any node which prevents constant +/// folding of the type name. +static bool containsProblematicNode(Demangle::Node *node, bool qualified) { + switch (node->getKind()) { + case Demangle::Node::Kind::LocalDeclName: + // The printing of contexts for local types is completely different + // in the runtime. Don't constant fold if we need to print the context. + if (qualified) + return true; + break; + case Demangle::Node::Kind::Class: { + // ObjC class names are not derived from the mangling but from the + // ObjC runtime. We cannot constant fold this. + Demangle::Node *context = node->getChild(0); + if (context->getKind() == Demangle::Node::Kind::Module && + context->getText() == "__C") { + return true; + } + break; + } + default: + break; + } + for (Demangle::Node *child : *node) { + if (containsProblematicNode(child, qualified)) + return true; + } + return false; +} + +/// Try to replace a _typeName() call with a constant string if the type is +/// statically known. +bool StringOptimization::optimizeTypeName(ApplyInst *typeNameCall) { + // Check, if the type is statically known. + auto *anyType = + dyn_cast(typeNameCall->getArgument(0)); + if (!anyType) + return false; + auto *metatypeInst = dyn_cast(anyType->getOperand()); + if (!metatypeInst) + return false; + + auto metatype = metatypeInst->getType().getAs(); + Type ty = metatype->getInstanceType(); + if (ty->hasArchetype()) + return false; + + // Usually the "qualified" parameter of _typeName() is a constant boolean. + Optional isQualifiedOpt = getIntConstant(typeNameCall->getArgument(1)); + if (!isQualifiedOpt) + return false; + bool isQualified = isQualifiedOpt.getValue(); + + // Create the constant type string by mangling + demangling. + Mangle::ASTMangler mangler; + std::string mangledTypeName = mangler.mangleTypeForTypeName(ty); + + Demangle::DemangleOptions options; + options.PrintForTypeName = true; + options.DisplayLocalNameContexts = false; + options.QualifyEntities = isQualified; + + Demangle::Context ctx; + Demangle::NodePointer root = ctx.demangleTypeAsNode(mangledTypeName); + if (!root || containsProblematicNode(root, isQualified)) + return false; + + std::string typeStr = nodeToString(root, options); + if (typeStr.empty()) + return false; + + ApplyInst *stringInit = createStringInit(typeStr, typeNameCall); + if (!stringInit) + return false; + + typeNameCall->replaceAllUsesWith(stringInit); + typeNameCall->eraseFromParent(); + + return true; +} + + +/// Returns the apply instruction if \p inst is a call of a function which has +/// a semantic attribute \p attr and exactly \p numArgs arguments. +ApplyInst *StringOptimization::isSemanticCall(SILInstruction *inst, + StringRef attr, unsigned numArgs) { + auto *apply = dyn_cast(inst); + if (!apply || apply->getNumArguments() != numArgs) + return nullptr; + + SILFunction *callee = apply->getReferencedFunctionOrNull(); + if (callee && callee->hasSemanticsAttr(attr)) + return apply; + + return nullptr; +} + +/// Returns true for all instructions which we can safely analyze as a potential +/// write to an identifyable objects. +/// +/// If we see any other kind of object user, which may write to an object, or +/// let the object address escape in some unexpected way (like address +/// projections), we'll just ignore that object and will not treat it as +/// "identifyable" object. +static bool mayWriteToIdentifyableObject(SILInstruction *inst) { + // For simplicity, only handle store and apply. This is sufficient for most + // case, especially for string interpolation. + return isa(inst) || isa(inst); +} + +/// Returns the store intstruction if \p inst is a store of a String to an +/// identifyable object. +StoreInst *StringOptimization:: +isStringStoreToIdentifyableObject(SILInstruction *inst) { + auto *store = dyn_cast(inst); + if (!store) + return nullptr; + if (store->getSrc()->getType() != stringType) + return nullptr; + + SILValue destAddr = store->getDest(); + // We only handle alloc_stack an indirect function arguments. For those we can + // be sure that they are not aliased, just by checking all users. + if (!isa(destAddr) && !isExclusiveArgument(destAddr)) + return nullptr; + + if (identifyableObjectsCache.count(destAddr) != 0) { + return identifyableObjectsCache[destAddr] ? store : nullptr; + } + + // Check if it's an "identifyable" object. This is the case if it only has + // users which we are able to track in a simple way: stores and applies. + for (Operand *use : destAddr->getUses()) { + SILInstruction *user = use->getUser(); + switch (user->getKind()) { + // Those instructions do not write to destAddr nor let they destAddr + // escape. + case SILInstructionKind::DebugValueAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::LoadInst: + break; + default: + if (!mayWriteToIdentifyableObject(user)) { + // We don't handle user. It is some instruction which may write to + // destAddr or let destAddr "escape" (like an address projection). + identifyableObjectsCache[destAddr] = false; + return nullptr; + } + break; + } + } + identifyableObjectsCache[destAddr] = true; + return store; +} + +/// Removes all objects from \p storedStrings which \p inst (potentially) +/// modifies. +void StringOptimization::invalidateModifiedObjects(SILInstruction *inst, + llvm::DenseMap &storedStrings) { + // Ignore non-writing instructions, like "load", "dealloc_stack". + // Note that identifyable objects (= keys in storedStrings) can only have + // certain kind of instructions as users: all instruction which we handle in + // isStringStoreToIdentifyableObject(). + if (!mayWriteToIdentifyableObject(inst)) + return; + + for (Operand &op : inst->getAllOperands()) { + storedStrings.erase(op.get()); + } +} + +/// Returns information about value if it's a constant string. +StringOptimization::StringInfo StringOptimization::getStringInfo(SILValue value) { + if (!value) + return StringInfo::unknown(); + + auto *apply = dyn_cast(value); + if (!apply) { + return getStringFromStaticLet(value); + } + + SILFunction *callee = apply->getReferencedFunctionOrNull(); + if (!callee) + return StringInfo::unknown(); + + if (callee->hasSemanticsAttr(semantics::STRING_INIT_EMPTY)) { + // An empty string initializer. + return StringInfo(""); + } + + if (callee->hasSemanticsAttr(semantics::STRING_INIT_EMPTY_WITH_CAPACITY)) { + // An empty string initializer with initial capacity. + int reservedCapacity = std::numeric_limits::max(); + if (apply->getNumArguments() > 0) { + if (Optional capacity = getIntConstant(apply->getArgument(0))) + reservedCapacity = capacity.getValue(); + } + return StringInfo("", reservedCapacity); + } + + if (callee->hasSemanticsAttr(semantics::STRING_MAKE_UTF8)) { + // A string literal initializer. + SILValue stringVal = apply->getArgument(0); + auto *stringLiteral = dyn_cast(stringVal); + SILValue lengthVal = apply->getArgument(1); + auto *intLiteral = dyn_cast(lengthVal); + if (intLiteral && stringLiteral && + // For simplicity, we only support UTF8 string literals. + stringLiteral->getEncoding() == StringLiteralInst::Encoding::UTF8 && + // This passed number of code units should always match the size of the + // string in the string literal. Just to be on the safe side, check it. + intLiteral->getValue() == stringLiteral->getValue().size()) { + return StringInfo(stringLiteral->getValue()); + } + } + return StringInfo::unknown(); +} + +/// Return the string if \p value is a load from a global static let, which is +/// initialized with a String constant. +StringOptimization::StringInfo +StringOptimization::getStringFromStaticLet(SILValue value) { + // Match the pattern + // %ptr_to_global = apply %addressor() + // %global_addr = pointer_to_address %ptr_to_global + // %value = load %global_addr + auto *load = dyn_cast(value); + if (!load) + return StringInfo::unknown(); + + auto *pta = dyn_cast(load->getOperand()); + if (!pta) + return StringInfo::unknown(); + + auto *addressorCall = dyn_cast(pta->getOperand()); + if (!addressorCall) + return StringInfo::unknown(); + + SILFunction *addressorFunc = addressorCall->getReferencedFunctionOrNull(); + if (!addressorFunc) + return StringInfo::unknown(); + + // The addressor function has a builtin.once call to the initializer. + BuiltinInst *onceCall = nullptr; + SILFunction *initializer = findInitializer(addressorFunc, onceCall); + if (!initializer) + return StringInfo::unknown(); + + if (initializer->size() != 1) + return StringInfo::unknown(); + + // Match the pattern + // %addr = global_addr @staticStringLet + // ... + // %str = apply %stringInitializer(...) + // store %str to %addr + GlobalAddrInst *gAddr = nullptr; + for (SILInstruction &inst : initializer->front()) { + if (auto *ga = dyn_cast(&inst)) { + if (gAddr) + return StringInfo::unknown(); + gAddr = ga; + } + } + if (!gAddr || !gAddr->getReferencedGlobal()->isLet()) + return StringInfo::unknown(); + + Operand *gUse = gAddr->getSingleUse(); + auto *store = dyn_cast(gUse->getUser()); + if (!store || store->getDest() != gAddr) + return StringInfo::unknown(); + + SILValue initVal = store->getSrc(); + + // This check is probably not needed, but let's be on the safe side: + // it prevents an infinite recursion if the initializer of the global is + // itself a load of another global, and so on. + if (isa(initVal)) + return StringInfo::unknown(); + + return getStringInfo(initVal); +} + +/// Returns the constant integer value if \a value is an Int or Bool struct with +/// an integer_literal as operand. +Optional StringOptimization::getIntConstant(SILValue value) { + auto *boolOrIntStruct = dyn_cast(value); + if (!boolOrIntStruct || boolOrIntStruct->getNumOperands() != 1) + return None; + + auto *literal = dyn_cast(boolOrIntStruct->getOperand(0)); + if (!literal || literal->getValue().getActiveBits() > 64) + return None; + + return literal->getValue().getSExtValue(); +} + +/// Replace a String.append() with a store of \p newValue to the destination. +void StringOptimization::replaceAppendWith(ApplyInst *appendCall, + SILValue newValue) { + SILBuilder builder(appendCall); + SILLocation loc = appendCall->getLoc(); + SILValue destAddr = appendCall->getArgument(1); + if (appendCall->getFunction()->hasOwnership()) { + builder.createStore(loc, newValue, destAddr, + StoreOwnershipQualifier::Assign); + } else { + builder.createDestroyAddr(loc, destAddr); + builder.createStore(loc, newValue, destAddr, + StoreOwnershipQualifier::Unqualified); + } + appendCall->eraseFromParent(); +} + +/// Returns a copy of \p value. Depending if the function is in OSSA, insert +/// either a copy_value or retain_value. +SILValue StringOptimization::copyValue(SILValue value, SILInstruction *before) { + SILBuilder builder(before); + SILLocation loc = before->getLoc(); + if (before->getFunction()->hasOwnership()) + return builder.createCopyValue(loc, value); + + builder.createRetainValue(loc, value, builder.getDefaultAtomicity()); + return value; +} + +/// Creates a call to a string initializer. +ApplyInst *StringOptimization::createStringInit(StringRef str, + SILInstruction *beforeInst) { + SILBuilder builder(beforeInst); + SILLocation loc = beforeInst->getLoc(); + SILModule &module = beforeInst->getFunction()->getModule(); + ASTContext &ctxt = module.getASTContext(); + + if (!makeUTF8Func) { + // Find the String initializer which takes a string_literal as argument. + ConstructorDecl *makeUTF8Decl = ctxt.getMakeUTF8StringDecl(); + if (!makeUTF8Decl) + return nullptr; + + auto Mangled = SILDeclRef(makeUTF8Decl, SILDeclRef::Kind::Allocator).mangle(); + makeUTF8Func = module.findFunction(Mangled, SILLinkage::PublicExternal); + if (!makeUTF8Func) + return nullptr; + } + + auto *literal = builder.createStringLiteral(loc, str, + StringLiteralInst::Encoding::UTF8); + + auto *length = builder.createIntegerLiteral(loc, + SILType::getBuiltinWordType(ctxt), + literal->getCodeUnitCount()); + + auto *isAscii = builder.createIntegerLiteral(loc, + SILType::getBuiltinIntegerType(1, ctxt), + intmax_t(ctxt.isASCIIString(str))); + + SILType stringMetaType = SILType::getPrimitiveObjectType( + CanType(MetatypeType::get(stringType.getASTType(), + MetatypeRepresentation::Thin))); + + auto *metaTypeInst = builder.createMetatype(loc, stringMetaType); + + auto *functionRef = builder.createFunctionRefFor(loc, makeUTF8Func); + + return builder.createApply(loc, functionRef, SubstitutionMap(), + { literal, length, isAscii, metaTypeInst }); +} + +/// The StringOptimization function pass. +class StringOptimizationPass : public SILFunctionTransform { +public: + + void run() override { + SILFunction *F = getFunction(); + if (!F->shouldOptimize()) + return; + + LLVM_DEBUG(llvm::dbgs() << "*** StringOptimization on function: " + << F->getName() << " ***\n"); + + StringOptimization stringOptimization; + bool changed = stringOptimization.run(F); + + if (changed) { + invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createStringOptimization() { + return new StringOptimizationPass(); +} diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 5a8cf979bcc48..6e4ab1b04bbbc 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -419,7 +419,7 @@ bool TempRValueOptPass::checkTempObjectDestroy( return false; // Look for a known destroy point as described in the function level - // comment. This whitelist can be expanded as more cases are handled in + // comment. This allowlist can be expanded as more cases are handled in // tryOptimizeCopyIntoTemp during copy replacement. SILInstruction *lastUser = &*std::prev(pos); if (isa(lastUser)) diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageAnalysisDumper.cpp new file mode 100644 index 0000000000000..a2684ee4356b1 --- /dev/null +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageAnalysisDumper.cpp @@ -0,0 +1,49 @@ +//===--- AccessedStorageAnalysisDumper.cpp - accessed storage anlaysis ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 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-accessed-storage-analysys-dumper" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Dumps per-function information on dynamically enforced formal accesses. +class AccessedStorageAnalysisDumper : public SILModuleTransform { + + void run() override { + auto *analysis = PM->getAnalysis(); + + for (auto &fn : *getModule()) { + llvm::outs() << "@" << fn.getName() << "\n"; + if (fn.empty()) { + llvm::outs() << "\n"; + continue; + } + const FunctionAccessedStorage &summary = analysis->getEffects(&fn); + summary.print(llvm::outs()); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createAccessedStorageAnalysisDumper() { + return new AccessedStorageAnalysisDumper(); +} diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp index 93c8af56ac13e..ee5208a4bc6aa 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -11,33 +11,44 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-accessed-storage-dumper" -#include "swift/SIL/SILArgument.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" -#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "llvm/Support/Debug.h" using namespace swift; +static void dumpAccessedStorage(SILInstruction *inst) { + visitAccessedAddress( + inst, + [&](Operand *operand) { + inst->print(llvm::outs()); + findAccessedStorage(operand->get()).print(llvm::outs()); + } + ); +} + namespace { -/// Dumps per-function information on dynamically enforced formal accesses. +/// Dumps sorage information for each access. class AccessedStorageDumper : public SILModuleTransform { void run() override { - auto *analysis = PM->getAnalysis(); - for (auto &fn : *getModule()) { llvm::outs() << "@" << fn.getName() << "\n"; if (fn.empty()) { llvm::outs() << "\n"; continue; } - const FunctionAccessedStorage &summary = analysis->getEffects(&fn); - summary.print(llvm::outs()); + for (auto &bb : fn) { + for (auto &inst : bb) { + if (inst.mayReadOrWriteMemory()) + dumpAccessedStorage(&inst); + } + } } } }; diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index aa187b7d15815..a8f823e584d5a 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -84,10 +84,11 @@ class BugReducerTester : public SILFunctionTransform { SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); auto FuncType = SILFunctionType::get( nullptr, - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - false /*isPseudoGeneric*/, false /*noescape*/, - DifferentiabilityKind::NonDifferentiable, - nullptr /*clangFunctionType*/), + SILFunctionType::ExtInfoBuilder( + SILFunctionType::Representation::Thin, false /*isPseudoGeneric*/, + false /*noescape*/, DifferentiabilityKind::NonDifferentiable, + nullptr /*clangFunctionType*/) + .build(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, ArrayRef(), ArrayRef(), ResultInfoArray, None, SubstitutionMap(), SubstitutionMap(), diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt index 2fa9f1fa0f2ea..871ae9f29d3ec 100644 --- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt +++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(swiftSILOptimizer PRIVATE AADumper.cpp AccessSummaryDumper.cpp + AccessedStorageAnalysisDumper.cpp AccessedStorageDumper.cpp BasicCalleePrinter.cpp BasicInstructionPropertyDumper.cpp diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index 6829541fe2ff6..28d760e070a91 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// +#include "swift/SIL/SILInstruction.h" #define DEBUG_TYPE "ConstExpr" #include "swift/SILOptimizer/Utils/ConstExpr.h" +#include "swift/AST/Builtins.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SubstitutionMap.h" @@ -1756,8 +1758,8 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { isa(inst) || isa(inst) || isa(inst) || isa(inst) || isa(inst) || - // Skip sanitizer instrumentation - isSanitizerInstrumentation(inst)) + // Skip instrumentation + isInstrumentation(inst)) return None; // If this is a special flow-sensitive instruction like a stack allocation, diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index defc2ad4ac920..84eaaf2d0cfc4 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1509,46 +1509,6 @@ static bool isFoldable(SILInstruction *I) { isa(I); } -bool ConstantFolder::constantFoldStringConcatenation(ApplyInst *AI) { - SILBuilder B(AI); - // Try to apply the string literal concatenation optimization. - auto *Concatenated = tryToConcatenateStrings(AI, B); - // Bail if string literal concatenation could not be performed. - if (!Concatenated) - return false; - - // Replace all uses of the old instruction by a new instruction. - AI->replaceAllUsesWith(Concatenated); - - auto RemoveCallback = [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }; - // Remove operands that are not used anymore. - // Even if they are apply_inst, it is safe to - // do so, because they can only be applies - // of functions annotated as string.utf16 - // or string.utf16. - for (auto &Op : AI->getAllOperands()) { - SILValue Val = Op.get(); - Op.drop(); - if (Val->use_empty()) { - auto *DeadI = Val->getDefiningInstruction(); - assert(DeadI); - recursivelyDeleteTriviallyDeadInstructions(DeadI, /*force*/ true, - RemoveCallback); - } - } - // Schedule users of the new instruction for constant folding. - // We only need to schedule the string.concat invocations. - for (auto AIUse : Concatenated->getUses()) { - if (isApplyOfStringConcat(*AIUse->getUser())) { - WorkList.insert(AIUse->getUser()); - } - } - // Delete the old apply instruction. - recursivelyDeleteTriviallyDeadInstructions(AI, /*force*/ true, - RemoveCallback); - return true; -} - /// Given a buitin instruction calling globalStringTablePointer, check whether /// the string passed to the builtin is constructed from a literal and if so, /// replace the uses of the builtin instruction with the string_literal inst. @@ -1772,16 +1732,6 @@ ConstantFolder::processWorkList() { } } - if (auto *AI = dyn_cast(I)) { - // Apply may only come from a string.concat invocation. - if (constantFoldStringConcatenation(AI)) { - // Invalidate all analysis that's related to the call graph. - InvalidateInstructions = true; - } - - continue; - } - // If we have a cast instruction, try to optimize it. if (isa(I) || isa(I) || isa(I) || diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 626c717a640d2..9092171488767 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -400,7 +400,7 @@ ConcreteExistentialInfo::ConcreteExistentialInfo(SILValue existential, // There is no ConcreteValue in this case. /// Determine the ExistentialConformances and SubstitutionMap. - ExistentialType = Protocol->getDeclaredType()->getCanonicalType(); + ExistentialType = Protocol->getDeclaredInterfaceType()->getCanonicalType(); initializeSubstitutionMap(ProtocolConformanceRef(ConcreteConformance), M); assert(isValid()); diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 11a5bf7e79af4..312c15d6e6984 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -43,6 +43,11 @@ using namespace swift; static llvm::cl::opt EnableExpandAll("enable-expand-all", llvm::cl::init(false)); +static llvm::cl::opt KeepWillThrowCall( + "keep-will-throw-call", llvm::cl::init(false), + llvm::cl::desc( + "Keep calls to swift_willThrow, even if the throw is optimized away")); + /// Creates an increment on \p Ptr before insertion point \p InsertPt that /// creates a strong_retain if \p Ptr has reference semantics itself or a /// retain_value if \p Ptr is a non-trivial value without reference-semantics. @@ -667,6 +672,91 @@ void swift::eraseUsesOfValue(SILValue v) { } } +SILValue swift:: +getConcreteValueOfExistentialBox(AllocExistentialBoxInst *existentialBox, + SILInstruction *ignoreUser) { + StoreInst *singleStore = nullptr; + for (Operand *use : getNonDebugUses(existentialBox)) { + SILInstruction *user = use->getUser(); + switch (user->getKind()) { + case SILInstructionKind::StrongRetainInst: + case SILInstructionKind::StrongReleaseInst: + break; + case SILInstructionKind::ProjectExistentialBoxInst: { + auto *projectedAddr = cast(user); + for (Operand *addrUse : getNonDebugUses(projectedAddr)) { + if (auto *store = dyn_cast(addrUse->getUser())) { + assert(store->getSrc() != projectedAddr && + "cannot store an address"); + // Bail if there are multiple stores. + if (singleStore) + return SILValue(); + singleStore = store; + continue; + } + // If there are other users to the box value address then bail out. + return SILValue(); + } + break; + } + case SILInstructionKind::BuiltinInst: { + auto *builtin = cast(user); + if (KeepWillThrowCall || + builtin->getBuiltinInfo().ID != BuiltinValueKind::WillThrow) { + return SILValue(); + } + break; + } + default: + if (user != ignoreUser) + return SILValue(); + break; + } + } + if (!singleStore) + return SILValue(); + return singleStore->getSrc(); +} + +SILValue swift:: +getConcreteValueOfExistentialBoxAddr(SILValue addr, SILInstruction *ignoreUser) { + auto *stackLoc = dyn_cast(addr); + if (!stackLoc) + return SILValue(); + + StoreInst *singleStackStore = nullptr; + for (Operand *stackUse : stackLoc->getUses()) { + SILInstruction *stackUser = stackUse->getUser(); + switch (stackUser->getKind()) { + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::DebugValueAddrInst: + case SILInstructionKind::LoadInst: + break; + case SILInstructionKind::StoreInst: { + auto *store = cast(stackUser); + assert(store->getSrc() != stackLoc && "cannot store an address"); + // Bail if there are multiple stores. + if (singleStackStore) + return SILValue(); + singleStackStore = store; + break; + } + default: + if (stackUser != ignoreUser) + return SILValue(); + break; + } + } + if (!singleStackStore) + return SILValue(); + + auto *box = dyn_cast(singleStackStore->getSrc()); + if (!box) + return SILValue(); + + return getConcreteValueOfExistentialBox(box, singleStackStore); +} + // Devirtualization of functions with covariant return types produces // a result that is not an apply, but takes an apply as an // argument. Attempt to dig the apply out from this result. @@ -1025,247 +1115,6 @@ swift::findInitAddressForTrivialEnum(UncheckedTakeEnumDataAddrInst *utedai) { return dyn_cast(singleUser); } -//===----------------------------------------------------------------------===// -// String Concatenation Optimizer -//===----------------------------------------------------------------------===// - -namespace { -/// This is a helper class that performs optimization of string literals -/// concatenation. -class StringConcatenationOptimizer { - /// Apply instruction being optimized. - ApplyInst *ai; - /// Builder to be used for creation of new instructions. - SILBuilder &builder; - /// Left string literal operand of a string concatenation. - StringLiteralInst *sliLeft = nullptr; - /// Right string literal operand of a string concatenation. - StringLiteralInst *sliRight = nullptr; - /// Function used to construct the left string literal. - FunctionRefInst *friLeft = nullptr; - /// Function used to construct the right string literal. - FunctionRefInst *friRight = nullptr; - /// Apply instructions used to construct left string literal. - ApplyInst *aiLeft = nullptr; - /// Apply instructions used to construct right string literal. - ApplyInst *aiRight = nullptr; - /// String literal conversion function to be used. - FunctionRefInst *friConvertFromBuiltin = nullptr; - /// Result type of a function producing the concatenated string literal. - SILValue funcResultType; - - /// Internal helper methods - bool extractStringConcatOperands(); - void adjustEncodings(); - APInt getConcatenatedLength(); - bool isAscii() const; - -public: - StringConcatenationOptimizer(ApplyInst *ai, SILBuilder &builder) - : ai(ai), builder(builder) {} - - /// Tries to optimize a given apply instruction if it is a - /// concatenation of string literals. - /// - /// Returns a new instruction if optimization was possible. - SingleValueInstruction *optimize(); -}; - -} // end anonymous namespace - -/// Checks operands of a string concatenation operation to see if -/// optimization is applicable. -/// -/// Returns false if optimization is not possible. -/// Returns true and initializes internal fields if optimization is possible. -bool StringConcatenationOptimizer::extractStringConcatOperands() { - auto *Fn = ai->getReferencedFunctionOrNull(); - if (!Fn) - return false; - - if (ai->getNumArguments() != 3 || !Fn->hasSemanticsAttr(semantics::STRING_CONCAT)) - return false; - - // Left and right operands of a string concatenation operation. - aiLeft = dyn_cast(ai->getOperand(1)); - aiRight = dyn_cast(ai->getOperand(2)); - - if (!aiLeft || !aiRight) - return false; - - friLeft = dyn_cast(aiLeft->getCallee()); - friRight = dyn_cast(aiRight->getCallee()); - - if (!friLeft || !friRight) - return false; - - auto *friLeftFun = friLeft->getReferencedFunctionOrNull(); - auto *friRightFun = friRight->getReferencedFunctionOrNull(); - - if (friLeftFun->getEffectsKind() >= EffectsKind::ReleaseNone - || friRightFun->getEffectsKind() >= EffectsKind::ReleaseNone) - return false; - - if (!friLeftFun->hasSemanticsAttrs() || !friRightFun->hasSemanticsAttrs()) - return false; - - auto aiLeftOperandsNum = aiLeft->getNumOperands(); - auto aiRightOperandsNum = aiRight->getNumOperands(); - - // makeUTF8 should have following parameters: - // (start: RawPointer, utf8CodeUnitCount: Word, isASCII: Int1) - if (!((friLeftFun->hasSemanticsAttr(semantics::STRING_MAKE_UTF8) - && aiLeftOperandsNum == 5) - || (friRightFun->hasSemanticsAttr(semantics::STRING_MAKE_UTF8) - && aiRightOperandsNum == 5))) - return false; - - sliLeft = dyn_cast(aiLeft->getOperand(1)); - sliRight = dyn_cast(aiRight->getOperand(1)); - - if (!sliLeft || !sliRight) - return false; - - // Only UTF-8 and UTF-16 encoded string literals are supported by this - // optimization. - if (sliLeft->getEncoding() != StringLiteralInst::Encoding::UTF8 - && sliLeft->getEncoding() != StringLiteralInst::Encoding::UTF16) - return false; - - if (sliRight->getEncoding() != StringLiteralInst::Encoding::UTF8 - && sliRight->getEncoding() != StringLiteralInst::Encoding::UTF16) - return false; - - return true; -} - -/// Ensures that both string literals to be concatenated use the same -/// UTF encoding. Converts UTF-8 into UTF-16 if required. -void StringConcatenationOptimizer::adjustEncodings() { - if (sliLeft->getEncoding() == sliRight->getEncoding()) { - friConvertFromBuiltin = friLeft; - if (sliLeft->getEncoding() == StringLiteralInst::Encoding::UTF8) { - funcResultType = aiLeft->getOperand(4); - } else { - funcResultType = aiLeft->getOperand(3); - } - return; - } - - builder.setCurrentDebugScope(ai->getDebugScope()); - - // If one of the string literals is UTF8 and another one is UTF16, - // convert the UTF8-encoded string literal into UTF16-encoding first. - if (sliLeft->getEncoding() == StringLiteralInst::Encoding::UTF8 - && sliRight->getEncoding() == StringLiteralInst::Encoding::UTF16) { - funcResultType = aiRight->getOperand(3); - friConvertFromBuiltin = friRight; - // Convert UTF8 representation into UTF16. - sliLeft = builder.createStringLiteral(ai->getLoc(), sliLeft->getValue(), - StringLiteralInst::Encoding::UTF16); - } - - if (sliRight->getEncoding() == StringLiteralInst::Encoding::UTF8 - && sliLeft->getEncoding() == StringLiteralInst::Encoding::UTF16) { - funcResultType = aiLeft->getOperand(3); - friConvertFromBuiltin = friLeft; - // Convert UTF8 representation into UTF16. - sliRight = builder.createStringLiteral(ai->getLoc(), sliRight->getValue(), - StringLiteralInst::Encoding::UTF16); - } - - // It should be impossible to have two operands with different - // encodings at this point. - assert( - sliLeft->getEncoding() == sliRight->getEncoding() - && "Both operands of string concatenation should have the same encoding"); -} - -/// Computes the length of a concatenated string literal. -APInt StringConcatenationOptimizer::getConcatenatedLength() { - // Real length of string literals computed based on its contents. - // Length is in code units. - auto sliLenLeft = sliLeft->getCodeUnitCount(); - (void)sliLenLeft; - auto sliLenRight = sliRight->getCodeUnitCount(); - (void)sliLenRight; - - // Length of string literals as reported by string.make functions. - auto *lenLeft = dyn_cast(aiLeft->getOperand(2)); - auto *lenRight = dyn_cast(aiRight->getOperand(2)); - - // Real and reported length should be the same. - assert(sliLenLeft == lenLeft->getValue() - && "Size of string literal in @_semantics(string.make) is wrong"); - - assert(sliLenRight == lenRight->getValue() - && "Size of string literal in @_semantics(string.make) is wrong"); - - // Compute length of the concatenated literal. - return lenLeft->getValue() + lenRight->getValue(); -} - -/// Computes the isAscii flag of a concatenated UTF8-encoded string literal. -bool StringConcatenationOptimizer::isAscii() const { - // Add the isASCII argument in case of UTF8. - // IsASCII is true only if IsASCII of both literals is true. - auto *asciiLeft = dyn_cast(aiLeft->getOperand(3)); - auto *asciiRight = dyn_cast(aiRight->getOperand(3)); - auto isAsciiLeft = asciiLeft->getValue() == 1; - auto isAsciiRight = asciiRight->getValue() == 1; - return isAsciiLeft && isAsciiRight; -} - -SingleValueInstruction *StringConcatenationOptimizer::optimize() { - // Bail out if string literals concatenation optimization is - // not possible. - if (!extractStringConcatOperands()) - return nullptr; - - // Perform string literal encodings adjustments if needed. - adjustEncodings(); - - // Arguments of the new StringLiteralInst to be created. - SmallVector arguments; - - // Encoding to be used for the concatenated string literal. - auto encoding = sliLeft->getEncoding(); - - // Create a concatenated string literal. - builder.setCurrentDebugScope(ai->getDebugScope()); - auto lv = sliLeft->getValue(); - auto rv = sliRight->getValue(); - auto *newSLI = - builder.createStringLiteral(ai->getLoc(), lv + Twine(rv), encoding); - arguments.push_back(newSLI); - - // Length of the concatenated literal according to its encoding. - auto *len = builder.createIntegerLiteral( - ai->getLoc(), aiLeft->getOperand(2)->getType(), getConcatenatedLength()); - arguments.push_back(len); - - // isAscii flag for UTF8-encoded string literals. - if (encoding == StringLiteralInst::Encoding::UTF8) { - bool ascii = isAscii(); - auto ilType = aiLeft->getOperand(3)->getType(); - auto *asciiLiteral = - builder.createIntegerLiteral(ai->getLoc(), ilType, intmax_t(ascii)); - arguments.push_back(asciiLiteral); - } - - // Type. - arguments.push_back(funcResultType); - - return builder.createApply(ai->getLoc(), friConvertFromBuiltin, - SubstitutionMap(), arguments); -} - -/// Top level entry point -SingleValueInstruction *swift::tryToConcatenateStrings(ApplyInst *ai, - SILBuilder &builder) { - return StringConcatenationOptimizer(ai, builder).optimize(); -} - //===----------------------------------------------------------------------===// // Closure Deletion //===----------------------------------------------------------------------===// @@ -1801,42 +1650,35 @@ void swift::replaceLoadSequence(SILInstruction *inst, SILValue value) { bool swift::calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl) { if (decl.isForeign) return false; - - if (decl.isEnumElement()) { - return calleesAreStaticallyKnowable(module, - cast(decl.getDecl())); - } - - auto *afd = decl.getAbstractFunctionDecl(); - assert(afd && "Expected abstract function decl!"); - return calleesAreStaticallyKnowable(module, afd); + return calleesAreStaticallyKnowable(module, decl.getDecl()); } /// Are the callees that could be called through Decl statically /// knowable based on the Decl and the compilation mode? -bool swift::calleesAreStaticallyKnowable(SILModule &module, - AbstractFunctionDecl *afd) { +bool swift::calleesAreStaticallyKnowable(SILModule &module, ValueDecl *vd) { + assert(isa(vd) || isa(vd)); + // Only handle members defined within the SILModule's associated context. - if (!afd->isChildContextOf(module.getAssociatedContext())) + if (!cast(vd)->isChildContextOf(module.getAssociatedContext())) return false; - if (afd->isDynamic()) { + if (vd->isDynamic()) { return false; } - if (!afd->hasAccess()) + if (!vd->hasAccess()) return false; // Only consider 'private' members, unless we are in whole-module compilation. - switch (afd->getEffectiveAccess()) { + switch (vd->getEffectiveAccess()) { case AccessLevel::Open: return false; case AccessLevel::Public: - if (isa(afd)) { + if (isa(vd)) { // Constructors are special: a derived class in another module can // "override" a constructor if its class is "open", although the // constructor itself is not open. - auto *nd = afd->getDeclContext()->getSelfNominalTypeDecl(); + auto *nd = vd->getDeclContext()->getSelfNominalTypeDecl(); if (nd->getEffectiveAccess() == AccessLevel::Open) return false; } @@ -1851,37 +1693,6 @@ bool swift::calleesAreStaticallyKnowable(SILModule &module, llvm_unreachable("Unhandled access level in switch."); } -/// Are the callees that could be called through Decl statically -/// knowable based on the Decl and the compilation mode? -// FIXME: Merge this with calleesAreStaticallyKnowable above -bool swift::calleesAreStaticallyKnowable(SILModule &module, - EnumElementDecl *eed) { - // Only handle members defined within the SILModule's associated context. - if (!eed->isChildContextOf(module.getAssociatedContext())) - return false; - - if (eed->isDynamic()) { - return false; - } - - if (!eed->hasAccess()) - return false; - - // Only consider 'private' members, unless we are in whole-module compilation. - switch (eed->getEffectiveAccess()) { - case AccessLevel::Open: - return false; - case AccessLevel::Public: - case AccessLevel::Internal: - return module.isWholeModule(); - case AccessLevel::FilePrivate: - case AccessLevel::Private: - return true; - } - - llvm_unreachable("Unhandled access level in switch."); -} - Optional swift::findLocalApplySites(FunctionRefBaseInst *fri) { SmallVector worklist(fri->use_begin(), fri->use_end()); diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index 9807b43eb5f6d..3a37e3f3430ba 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -243,7 +243,6 @@ FunctionSignatureSpecializationMangler::mangleConstantProp(LiteralInst *LI) { switch (SLI->getEncoding()) { case StringLiteralInst::Encoding::Bytes: ArgOpBuffer << 'B'; break; case StringLiteralInst::Encoding::UTF8: ArgOpBuffer << 'b'; break; - case StringLiteralInst::Encoding::UTF16: ArgOpBuffer << 'w'; break; case StringLiteralInst::Encoding::ObjCSelector: ArgOpBuffer << 'c'; break; } break; diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index f0bfb34c3e621..4c976d26a0f1d 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -19,6 +19,7 @@ #include "MiscDiagnostics.h" #include "SolutionResult.h" #include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/NameLookup.h" @@ -38,6 +39,24 @@ using namespace constraints; namespace { +/// Find the first #available condition within the statement condition, +/// or return NULL if there isn't one. +const StmtConditionElement *findAvailabilityCondition(StmtCondition stmtCond) { + for (const auto &cond : stmtCond) { + switch (cond.getKind()) { + case StmtConditionElement::CK_Boolean: + case StmtConditionElement::CK_PatternBinding: + continue; + + case StmtConditionElement::CK_Availability: + return &cond; + break; + } + } + + return nullptr; +} + /// Visitor to classify the contents of the given closure. class BuilderClosureVisitor : private StmtVisitor { @@ -80,10 +99,21 @@ class BuilderClosureVisitor auto simplifiedTy = cs->simplifyType(builderType); if (!simplifiedTy->hasTypeVariable()) { typeExpr = TypeExpr::createImplicitHack(loc, simplifiedTy, ctx); + } else if (auto *decl = simplifiedTy->getAnyGeneric()) { + // HACK: If there's not enough information to completely resolve the + // builder type, but we have the base available to us, form an *explicit* + // TypeExpr pointing at it. We cannot form an implicit base without + // a fully-resolved concrete type. Really, whatever we put here has no + // bearing on the generated solution because we're going to use this node + // to stash the builder type and hand it back to the ambient + // constraint system. + typeExpr = TypeExpr::createForDecl(DeclNameLoc(loc), decl, dc); } else { // HACK: If there's not enough information in the constraint system, // create a garbage base type to force it to diagnose // this as an ambiguous expression. + // FIXME: We can also construct an UnresolvedMemberExpr here instead of + // an UnresolvedDotExpr and get a slightly better diagnostic. typeExpr = TypeExpr::createImplicitHack(loc, ErrorType::get(ctx), ctx); } cs->setType(typeExpr, MetatypeType::get(builderType)); @@ -197,9 +227,6 @@ class BuilderClosureVisitor DeclContext *dc, Type builderType, Type bodyResultType) : cs(cs), dc(dc), ctx(ctx), builderType(builderType) { - assert((cs || !builderType->hasTypeVariable()) && - "cannot handle builder type with type variables without " - "constraint system"); builder = builderType->getAnyNominal(); applied.builderType = builderType; applied.bodyResultType = bodyResultType; @@ -487,10 +514,20 @@ class BuilderClosureVisitor if (!cs || !thenVar || (elseChainVar && !*elseChainVar)) return nullptr; + // If there is a #available in the condition, the 'then' will need to + // be wrapped in a call to buildLimitedAvailability(_:), if available. + Expr *thenVarRefExpr = buildVarRef( + thenVar, ifStmt->getThenStmt()->getEndLoc()); + if (findAvailabilityCondition(ifStmt->getCond()) && + builderSupports(ctx.Id_buildLimitedAvailability)) { + thenVarRefExpr = buildCallIfWanted( + ifStmt->getThenStmt()->getEndLoc(), ctx.Id_buildLimitedAvailability, + { thenVarRefExpr }, { Identifier() }); + } + // Prepare the `then` operand by wrapping it to produce a chain result. Expr *thenExpr = buildWrappedChainPayload( - buildVarRef(thenVar, ifStmt->getThenStmt()->getEndLoc()), - payloadIndex, numPayloads, isOptional); + thenVarRefExpr, payloadIndex, numPayloads, isOptional); // Prepare the `else operand: Expr *elseExpr; @@ -662,6 +699,18 @@ class BuilderClosureVisitor if (!cs) return nullptr; + // If there are no 'case' statements in the body let's try + // to diagnose this situation via limited exhaustiveness check + // before failing a builder transform, otherwise type-checker + // might end up without any diagnostics which leads to crashes + // in SILGen. + if (capturedCaseVars.empty()) { + TypeChecker::checkSwitchExhaustiveness(switchStmt, dc, + /*limitChecking=*/true); + hadError = true; + return nullptr; + } + // Form the expressions that inject the result of each case into the // appropriate llvm::TinyPtrVector injectedCaseExprs; @@ -710,6 +759,18 @@ class BuilderClosureVisitor } VarDecl *visitCaseStmt(CaseStmt *caseStmt, Expr *subjectExpr) { + auto *body = caseStmt->getBody(); + + // Explicitly disallow `case` statements with empty bodies + // since that helps to diagnose other issues with switch + // statements by excluding invalid cases. + if (auto *BS = dyn_cast(body)) { + if (BS->getNumElements() == 0) { + hadError = true; + return nullptr; + } + } + // If needed, generate constraints for everything in the case statement. if (cs) { auto locator = cs->getConstraintLocator( @@ -1164,6 +1225,36 @@ class BuilderClosureRewriter capturedThen.first, {capturedThen.second.front()})); ifStmt->setThenStmt(newThen); + // Look for a #available condition. If there is one, we need to check + // that the resulting type of the "then" doesn't refer to any types that + // are unavailable in the enclosing context. + // + // Note that this is for staging in support for buildLimitedAvailability(); + // the diagnostic is currently a warning, so that existing code that + // compiles today will continue to compile. Once function builder types + // have had the chance to adopt buildLimitedAvailability(), we'll upgrade + // this warning to an error. + if (auto availabilityCond = findAvailabilityCondition(ifStmt->getCond())) { + SourceLoc loc = availabilityCond->getStartLoc(); + Type thenBodyType = solution.simplifyType( + solution.getType(target.captured.second[0])); + thenBodyType.findIf([&](Type type) { + auto nominal = type->getAnyNominal(); + if (!nominal) + return false; + + if (auto reason = TypeChecker::checkDeclarationAvailability( + nominal, loc, dc)) { + ctx.Diags.diagnose( + loc, diag::function_builder_missing_limited_availability, + builderTransform.builderType); + return true; + } + + return false; + }); + } + if (auto elseBraceStmt = dyn_cast_or_null(ifStmt->getElseStmt())) { // Translate the "else" branch when it's a stmt-brace. @@ -1257,8 +1348,7 @@ class BuilderClosureRewriter // Check restrictions on '@unknown'. if (caseStmt->hasUnknownAttr()) { checkUnknownAttrRestrictions( - cs.getASTContext(), caseStmt, /*fallthroughDest=*/nullptr, - limitExhaustivityChecks); + cs.getASTContext(), caseStmt, limitExhaustivityChecks); } ++caseIndex; @@ -1387,64 +1477,6 @@ BraceStmt *swift::applyFunctionBuilderTransform( captured.first, captured.second))); } -/// Produce any additional syntactic diagnostics for the body of a function -/// that had a function builder applied. -static void performAddOnDiagnostics(BraceStmt *stmt, DeclContext *dc) { - class AddOnDiagnosticWalker : public ASTWalker { - SmallVector dcStack; - - public: - AddOnDiagnosticWalker(DeclContext *dc) { - dcStack.push_back(dc); - } - - std::pair walkToExprPre(Expr *expr) override { - performSyntacticExprDiagnostics( - expr, dcStack.back(), /*isExprStmt=*/false); - - if (auto closure = dyn_cast(expr)) { - if (closure->wasSeparatelyTypeChecked()) { - dcStack.push_back(closure); - return { true, expr }; - } - } - - return { false, expr }; - } - - Expr *walkToExprPost(Expr *expr) override { - if (auto closure = dyn_cast(expr)) { - if (closure->wasSeparatelyTypeChecked()) { - assert(dcStack.back() == closure); - dcStack.pop_back(); - } - } - - return expr; - } - - std::pair walkToStmtPre(Stmt *stmt) override { - performStmtDiagnostics(dcStack.back()->getASTContext(), stmt); - return { true, stmt }; - } - - std::pair walkToPatternPre(Pattern *pattern) override { - return { false, pattern }; - } - - bool walkToTypeLocPre(TypeLoc &typeLoc) override { return false; } - - bool walkToTypeReprPre(TypeRepr *typeRepr) override { return false; } - - bool walkToParameterListPre(ParameterList *params) override { - return false; - } - }; - - AddOnDiagnosticWalker walker(dc); - stmt->walk(walker); -} - Optional TypeChecker::applyFunctionBuilderBodyTransform( FuncDecl *func, Type builderType) { // Pre-check the body: pre-check any expressions in it and look @@ -1453,7 +1485,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // 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}; + auto request = PreCheckFunctionBuilderRequest{AnyFunctionRef(func)}; switch (evaluateOrDefault( ctx.evaluator, request, FunctionBuilderBodyPreCheck::Error)) { case FunctionBuilderBodyPreCheck::Okay: @@ -1520,7 +1552,6 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( if (auto result = cs.matchFunctionBuilder( func, builderType, resultContextType, resultConstraintKind, - cs.getConstraintLocator(func->getBody()), cs.getConstraintLocator(func->getBody()))) { if (result->isFailure()) return nullptr; @@ -1556,6 +1587,13 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // The system was salvaged; continue on as if nothing happened. } + if (cs.isDebugMode()) { + auto &log = llvm::errs(); + log << "--- Applying Solution ---\n"; + solutions.front().dump(log); + log << '\n'; + } + // FIXME: Shouldn't need to do this. cs.applySolution(solutions.front()); @@ -1563,7 +1601,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( if (auto result = cs.applySolution( solutions.front(), SolutionApplicationTarget(func))) { - performAddOnDiagnostics(result->getFunctionBody(), func); + performSyntacticDiagnosticsForTarget(*result, /*isExprStmt*/ false); return result->getFunctionBody(); } @@ -1574,7 +1612,7 @@ Optional ConstraintSystem::matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, - ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { + ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad function builder type"); assert(builder->getAttrs().hasAttribute()); @@ -1624,22 +1662,6 @@ ConstraintSystem::matchFunctionBuilder( } } - // If the builder type has a type parameter, substitute in the type - // variables. - if (builderType->hasTypeParameter()) { - // Find the opened type for this callee and substitute in the type - // parametes. - for (const auto &opened : OpenedTypes) { - if (opened.first == calleeLocator) { - OpenedTypeMap replacements(opened.second.begin(), - opened.second.end()); - builderType = openType(builderType, replacements); - break; - } - } - assert(!builderType->hasTypeParameter()); - } - BuilderClosureVisitor visitor(getASTContext(), this, dc, builderType, bodyResultType); @@ -1664,8 +1686,12 @@ ConstraintSystem::matchFunctionBuilder( // If builder is applied to the closure expression then // `closure body` to `closure result` matching should // use special locator. - if (auto *closure = fn.getAbstractClosureExpr()) + if (auto *closure = fn.getAbstractClosureExpr()) { locator = getConstraintLocator(closure, ConstraintLocator::ClosureResult); + } else { + locator = getConstraintLocator(fn.getAbstractFunctionDecl(), + ConstraintLocator::FunctionBuilderBodyResult); + } // Bind the body result type to the type of the transformed expression. addConstraint(bodyResultConstraintKind, transformedType, bodyResultType, @@ -1701,11 +1727,11 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { if (HasError) return FunctionBuilderBodyPreCheck::Error; + assert(oldBody == newBody && "pre-check walk wasn't in-place?"); + if (hasReturnStmt()) return FunctionBuilderBodyPreCheck::HasReturnStmt; - assert(oldBody == newBody && "pre-check walk wasn't in-place?"); - return FunctionBuilderBodyPreCheck::Okay; } @@ -1743,17 +1769,16 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { } -FunctionBuilderBodyPreCheck -PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, - AnyFunctionRef fn) const { +FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( + Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const { // We don't want to do the precheck if it will already have happened in // the enclosing expression. bool skipPrecheck = false; if (auto closure = dyn_cast_or_null( - fn.getAbstractClosureExpr())) + owner.Fn.getAbstractClosureExpr())) skipPrecheck = shouldTypeCheckInEnclosingExpression(closure); - return PreCheckFunctionBuilderApplication(fn, false).run(); + return PreCheckFunctionBuilderApplication(owner.Fn, false).run(); } std::vector TypeChecker::findReturnStatements(AnyFunctionRef fn) { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 36ee5f7a6c501..c24f5b0eeac43 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -237,6 +237,9 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, // Don't bother building the key path string if the key path didn't even // resolve. return false; + case KeyPathExpr::Component::Kind::DictionaryKey: + llvm_unreachable("DictionaryKey only valid in #keyPath expressions."); + return false; } } @@ -979,7 +982,7 @@ namespace { Expr *closureBody = closureCall; closureBody = coerceToType(closureCall, resultTy, locator); - if (selfFnTy->getExtInfo().throws()) { + if (selfFnTy->getExtInfo().isThrowing()) { closureBody = new (context) TryExpr(closureBody->getStartLoc(), closureBody, cs.getType(closureBody), /*implicit=*/true); @@ -1529,8 +1532,6 @@ namespace { /// \param callee The callee for the function being applied. /// \param apply The ApplyExpr that forms the call. /// \param argLabels The argument labels provided for the call. - /// \param hasTrailingClosure Whether the last argument is a trailing - /// closure. /// \param locator Locator used to describe where in this expression we are. /// /// \returns the coerced expression, which will have type \c ToType. @@ -1538,7 +1539,6 @@ namespace { coerceCallArguments(Expr *arg, AnyFunctionType *funcType, ConcreteDeclRef callee, ApplyExpr *apply, ArrayRef argLabels, - bool hasTrailingClosure, ConstraintLocatorBuilder locator); /// Coerce the given object argument (e.g., for the base of a @@ -1749,7 +1749,7 @@ namespace { auto fullSubscriptTy = openedFullFnType->getResult() ->castTo(); index = coerceCallArguments(index, fullSubscriptTy, subscriptRef, nullptr, - argLabels, hasTrailingClosure, + argLabels, locator.withPathElement( ConstraintLocator::ApplyArgument)); if (!index) @@ -2512,17 +2512,16 @@ namespace { Expr *visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) { switch (expr->getKind()) { - case MagicIdentifierLiteralExpr::File: - case MagicIdentifierLiteralExpr::FilePath: - case MagicIdentifierLiteralExpr::Function: +#define MAGIC_STRING_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: \ return handleStringLiteralExpr(expr); - - case MagicIdentifierLiteralExpr::Line: - case MagicIdentifierLiteralExpr::Column: +#define MAGIC_INT_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: \ return handleIntegerLiteralExpr(expr); - - case MagicIdentifierLiteralExpr::DSOHandle: +#define MAGIC_POINTER_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ + case MagicIdentifierLiteralExpr::NAME: \ return expr; +#include "swift/AST/MagicIdentifierKinds.def" } @@ -2574,7 +2573,6 @@ namespace { auto newArg = coerceCallArguments( expr->getArg(), fnType, witness, /*applyExpr=*/nullptr, expr->getArgumentLabels(), - expr->hasTrailingClosure(), cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument)); expr->setInitializer(witness); @@ -3575,12 +3573,14 @@ namespace { castKind == CheckedCastKind::ArrayDowncast || castKind == CheckedCastKind::DictionaryDowncast || castKind == CheckedCastKind::SetDowncast) { - auto *const cast = ConditionalCheckedCastExpr::createImplicit( - ctx, sub, castTypeRepr, toType); + auto *const cast = + ConditionalCheckedCastExpr::createImplicit(ctx, sub, toType); + cast->setType(OptionalType::get(toType)); + cast->setCastType(toType); cs.setType(cast, cast->getType()); // Type-check this conditional case. - Expr *result = handleConditionalCheckedCastExpr(cast, true); + Expr *result = handleConditionalCheckedCastExpr(cast, castTypeRepr); if (!result) return nullptr; @@ -3589,14 +3589,18 @@ namespace { // Match the optional value against its `Some` case. auto *someDecl = ctx.getOptionalSomeDecl(); - auto isSomeExpr = new (ctx) EnumIsCaseExpr(result, someDecl); + auto isSomeExpr = + new (ctx) EnumIsCaseExpr(result, castTypeRepr, someDecl); auto boolDecl = ctx.getBoolDecl(); if (!boolDecl) { ctx.Diags.diagnose(SourceLoc(), diag::broken_bool); } - cs.setType(isSomeExpr, boolDecl ? boolDecl->getDeclaredType() : Type()); + cs.setType(isSomeExpr, + boolDecl + ? boolDecl->getDeclaredInterfaceType() + : Type()); return isSomeExpr; } @@ -4011,10 +4015,16 @@ namespace { } Expr *visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) { + // Simplify and update the type we're casting to. + auto *const castTypeRepr = expr->getCastTypeRepr(); + const auto toType = simplifyType(cs.getType(castTypeRepr)); + expr->setCastType(toType); + cs.setType(castTypeRepr, toType); + // If we need to insert a force-unwrap for coercions of the form // 'as! T!', do so now. if (hasForcedOptionalResult(expr)) { - auto *coerced = handleConditionalCheckedCastExpr(expr); + auto *coerced = handleConditionalCheckedCastExpr(expr, castTypeRepr); if (!coerced) return nullptr; @@ -4022,29 +4032,28 @@ namespace { coerced, cs.getType(coerced)->getOptionalObjectType()); } - return handleConditionalCheckedCastExpr(expr); + return handleConditionalCheckedCastExpr(expr, castTypeRepr); } Expr *handleConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr, - bool isInsideIsExpr = false) { + TypeRepr *castTypeRepr) { + assert(castTypeRepr && + "cast requires TypeRepr; implicit casts are superfluous"); + // The subexpression is always an rvalue. auto &ctx = cs.getASTContext(); auto sub = cs.coerceToRValue(expr->getSubExpr()); expr->setSubExpr(sub); // Simplify and update the type we're casting to. - auto *const castTypeRepr = expr->getCastTypeRepr(); - const auto fromType = cs.getType(sub); - const auto toType = simplifyType(cs.getType(castTypeRepr)); - expr->setCastType(toType); - cs.setType(castTypeRepr, toType); + const auto toType = expr->getCastType(); bool isSubExprLiteral = isa(sub); auto castContextKind = - (SuppressDiagnostics || isInsideIsExpr || isSubExprLiteral) - ? CheckedCastContextKind::None - : CheckedCastContextKind::ConditionalCast; + (SuppressDiagnostics || expr->isImplicit() || isSubExprLiteral) + ? CheckedCastContextKind::None + : CheckedCastContextKind::ConditionalCast; auto castKind = TypeChecker::typeCheckCheckedCast( fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, @@ -4126,6 +4135,10 @@ namespace { // If we end up here, we should have diagnosed somewhere else // already. Expr *simplified = simplifyExprType(expr); + // Invalidate 'VarDecl's inside the pattern. + expr->getSubPattern()->forEachVariable([](VarDecl *VD) { + VD->setInvalid(); + }); if (!SuppressDiagnostics && !cs.getType(simplified)->is()) { auto &de = cs.getASTContext().Diags; @@ -4680,6 +4693,10 @@ namespace { case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::TupleElement: llvm_unreachable("already resolved"); + break; + case KeyPathExpr::Component::Kind::DictionaryKey: + llvm_unreachable("DictionaryKey only valid in #keyPath"); + break; } // Update "componentTy" with the result type of the last component. @@ -4919,7 +4936,7 @@ namespace { auto *newIndexExpr = coerceCallArguments(indexExpr, subscriptType, ref, /*applyExpr*/ nullptr, labels, - /*hasTrailingClosure*/ false, locator); + locator); // We need to be able to hash the captured index values in order for // KeyPath itself to be hashable, so check that all of the subscript @@ -5398,7 +5415,7 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType, Expr *ExprRewriter::coerceImplicitlyUnwrappedOptionalToValue(Expr *expr, Type objTy) { auto optTy = cs.getType(expr); - // Coerce to an r-value. + // Preserve l-valueness of the result. if (optTy->is()) objTy = LValueType::get(objTy); @@ -5452,12 +5469,130 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee, return false; } -Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, - ConcreteDeclRef callee, - ApplyExpr *apply, - ArrayRef argLabels, - bool hasTrailingClosure, - ConstraintLocatorBuilder locator) { +/// Attach a Fix-It to the given diagnostic to give the trailing closure +/// argument a label. +static void labelTrailingClosureArgument( + ASTContext &ctx, Expr *fn, Expr *arg, Identifier paramName, + Expr *trailingClosure, InFlightDiagnostic &diag) { + // Dig out source locations. + SourceLoc existingRParenLoc; + SourceLoc leadingCommaLoc; + if (auto tupleExpr = dyn_cast(arg)) { + existingRParenLoc = tupleExpr->getRParenLoc(); + assert(tupleExpr->getNumElements() >= 2 && "Should be a ParenExpr?"); + leadingCommaLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, + tupleExpr->getElements()[tupleExpr->getNumElements()-2]->getEndLoc()); + } else { + auto parenExpr = cast(arg); + existingRParenLoc = parenExpr->getRParenLoc(); + } + + // Figure out the text to be inserted before the trailing closure. + SmallString<16> insertionText; + SourceLoc insertionLoc; + if (leadingCommaLoc.isValid()) { + insertionText += ", "; + assert(existingRParenLoc.isValid()); + insertionLoc = leadingCommaLoc; + } else if (existingRParenLoc.isInvalid()) { + insertionText += "("; + insertionLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, fn->getEndLoc()); + } else { + insertionLoc = existingRParenLoc; + } + + // Add the label, if there is one. + if (!paramName.empty()) { + insertionText += paramName.str(); + insertionText += ": "; + } + + // If there is an existing right parentheses, remove it while we + // insert the new text. + if (existingRParenLoc.isValid()) { + SourceLoc afterExistingRParenLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, existingRParenLoc); + diag.fixItReplaceChars( + insertionLoc, afterExistingRParenLoc, insertionText); + } else { + // Insert the appropriate prefix. + diag.fixItInsert(insertionLoc, insertionText); + } + + // Insert a right parenthesis after the closing '}' of the trailing closure; + SourceLoc newRParenLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, trailingClosure->getEndLoc()); + diag.fixItInsert(newRParenLoc, ")"); +} + +/// Find the trailing closure argument of a tuple or parenthesized expression. +/// +/// Due to a quirk of the backward scan that could allow reordering of +/// arguments in the presence of a trailing closure, it might not be the last +/// argument in the tuple. +static Expr *findTrailingClosureArgument(Expr *arg) { + if (auto parenExpr = dyn_cast(arg)) { + return parenExpr->getSubExpr(); + } + + auto tupleExpr = cast(arg); + SourceLoc endLoc = tupleExpr->getEndLoc(); + for (Expr *elt : llvm::reverse(tupleExpr->getElements())) { + if (elt->getEndLoc() == endLoc) + return elt; + } + + return tupleExpr->getElements().back(); +} + +/// Find the index of the parameter that binds the given argument. +static unsigned findParamBindingArgument( + ArrayRef parameterBindings, unsigned argIndex) { + for (unsigned paramIdx : indices(parameterBindings)) { + if (llvm::find(parameterBindings[paramIdx], argIndex) + != parameterBindings[paramIdx].end()) + return paramIdx; + } + + llvm_unreachable("No parameter binds the argument?"); +} + +/// Warn about the use of the deprecated "backward" scan for matching the +/// unlabeled trailing closure. It was needed to properly type check, but +/// this code will break with a future version of Swift. +static void warnAboutTrailingClosureBackwardScan( + ConcreteDeclRef callee, Expr *fn, Expr *arg, + ArrayRef params, + Optional unlabeledTrailingClosureIndex, + ArrayRef parameterBindings) { + + Expr *trailingClosure = findTrailingClosureArgument(arg); + unsigned paramIdx = findParamBindingArgument( + parameterBindings, *unlabeledTrailingClosureIndex); + ASTContext &ctx = params[paramIdx].getPlainType()->getASTContext(); + Identifier paramName = params[paramIdx].getLabel(); + + { + auto diag = ctx.Diags.diagnose( + trailingClosure->getStartLoc(), + diag::unlabeled_trailing_closure_deprecated, paramName); + labelTrailingClosureArgument( + ctx, fn, arg, paramName, trailingClosure, diag); + } + + if (auto decl = callee.getDecl()) { + ctx.Diags.diagnose(decl, diag::decl_declared_here, decl->getName()); + } +} + +Expr *ExprRewriter::coerceCallArguments( + Expr *arg, AnyFunctionType *funcType, + ConcreteDeclRef callee, + ApplyExpr *apply, + ArrayRef argLabels, + ConstraintLocatorBuilder locator) { auto &ctx = getConstraintSystem().getASTContext(); auto params = funcType->getParams(); @@ -5488,14 +5623,17 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, bool shouldInjectWrappedValuePlaceholder = target->shouldInjectWrappedValuePlaceholder(apply); - auto injectWrappedValuePlaceholder = [&](Expr *arg) -> Expr * { - auto *placeholder = PropertyWrapperValuePlaceholderExpr::create(ctx, - arg->getSourceRange(), cs.getType(arg), arg); - cs.cacheType(placeholder); - cs.cacheType(placeholder->getOpaqueValuePlaceholder()); - shouldInjectWrappedValuePlaceholder = false; - return placeholder; - }; + auto injectWrappedValuePlaceholder = + [&](Expr *arg, bool isAutoClosure = false) -> Expr * { + auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( + ctx, arg->getSourceRange(), cs.getType(arg), + target->propertyWrapperHasInitialWrappedValue() ? arg : nullptr, + isAutoClosure); + cs.cacheType(placeholder); + cs.cacheType(placeholder->getOpaqueValuePlaceholder()); + shouldInjectWrappedValuePlaceholder = false; + return placeholder; + }; // Quickly test if any further fix-ups for the argument types are necessary. if (AnyFunctionType::equalParams(args, params) && @@ -5506,23 +5644,47 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, AnyFunctionType::relabelParams(args, argLabels); MatchCallArgumentListener listener; - SmallVector parameterBindings; - bool failed = constraints::matchCallArguments(args, params, - paramInfo, - arg->getUnlabeledTrailingClosureIndexOfPackedArgument(), - /*allowFixes=*/false, listener, - parameterBindings); - - assert((matchCanFail || !failed) && "Call arguments did not match up?"); - (void)failed; + auto unlabeledTrailingClosureIndex = + arg->getUnlabeledTrailingClosureIndexOfPackedArgument(); + + // Determine the trailing closure matching rule that was applied. This + // is only relevant for explicit calls and subscripts. + auto trailingClosureMatching = TrailingClosureMatching::Forward; + { + SmallVector path; + auto anchor = locator.getLocatorParts(path); + if (!path.empty() && path.back().is() && + (anchor.isExpr(ExprKind::Call) || anchor.isExpr(ExprKind::Subscript))) { + auto locatorPtr = cs.getConstraintLocator(locator); + assert(solution.trailingClosureMatchingChoices.count(locatorPtr) == 1); + trailingClosureMatching = solution.trailingClosureMatchingChoices.find( + locatorPtr)->second; + } + } + + auto callArgumentMatch = constraints::matchCallArguments( + args, params, paramInfo, unlabeledTrailingClosureIndex, + /*allowFixes=*/false, listener, trailingClosureMatching); + + assert((matchCanFail || callArgumentMatch) && + "Call arguments did not match up?"); (void)matchCanFail; + auto parameterBindings = std::move(callArgumentMatch->parameterBindings); + // We should either have parentheses or a tuple. auto *argTuple = dyn_cast(arg); auto *argParen = dyn_cast(arg); // FIXME: Eventually, we want to enforce that we have either argTuple or // argParen here. + // Warn about the backward scan being deprecated. + if (trailingClosureMatching == TrailingClosureMatching::Backward) { + warnAboutTrailingClosureBackwardScan( + callee, apply ? apply->getFn() : nullptr, arg, params, + unlabeledTrailingClosureIndex, parameterBindings); + } + SourceLoc lParenLoc, rParenLoc; if (argTuple) { lParenLoc = argTuple->getLParenLoc(); @@ -5690,19 +5852,18 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, locator.withPathElement(ConstraintLocator::AutoclosureResult)); if (shouldInjectWrappedValuePlaceholder) { - // If init(wrappedValue:) takes an escaping autoclosure, then we want + // If init(wrappedValue:) takes an autoclosure, then we want // the effect of autoclosure forwarding of the placeholder // autoclosure. The only way to do this is to call the placeholder // autoclosure when passing it to the init. - if (!closureType->isNoEscape()) { - auto *placeholder = injectWrappedValuePlaceholder( - cs.buildAutoClosureExpr(arg, closureType)); - arg = CallExpr::createImplicit(ctx, placeholder, {}, {}); - arg->setType(closureType->getResult()); - cs.cacheType(arg); - } else { - arg = injectWrappedValuePlaceholder(arg); - } + bool isDefaultWrappedValue = + target->propertyWrapperHasInitialWrappedValue(); + auto *placeholder = injectWrappedValuePlaceholder( + cs.buildAutoClosureExpr(arg, closureType, isDefaultWrappedValue), + /*isAutoClosure=*/true); + arg = CallExpr::createImplicit(ctx, placeholder, {}, {}); + arg->setType(closureType->getResult()); + cs.cacheType(arg); } convertedArg = cs.buildAutoClosureExpr(arg, closureType); @@ -5739,7 +5900,7 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, (params[0].getValueOwnership() == ValueOwnership::Default || params[0].getValueOwnership() == ValueOwnership::InOut)) { assert(newArgs.size() == 1); - assert(!hasTrailingClosure); + assert(!unlabeledTrailingClosureIndex); return newArgs[0]; } @@ -5752,8 +5913,9 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, argParen->setSubExpr(newArgs[0]); } else { bool isImplicit = arg->isImplicit(); - arg = new (ctx) - ParenExpr(lParenLoc, newArgs[0], rParenLoc, hasTrailingClosure); + arg = new (ctx) ParenExpr( + lParenLoc, newArgs[0], rParenLoc, + static_cast(unlabeledTrailingClosureIndex)); arg->setImplicit(isImplicit); } } else { @@ -5767,8 +5929,8 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, } } else { // Build a new TupleExpr, re-using source location information. - arg = TupleExpr::create(ctx, lParenLoc, newArgs, newLabels, newLabelLocs, - rParenLoc, hasTrailingClosure, + arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, newArgs, newLabels, + newLabelLocs, unlabeledTrailingClosureIndex, /*implicit=*/arg->isImplicit()); } } @@ -6580,7 +6742,9 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, maybeDiagnoseUnsupportedDifferentiableConversion(cs, expr, toFunc); if (!isFromDifferentiable && isToDifferentiable) { auto newEI = - fromEI.withDifferentiabilityKind(toEI.getDifferentiabilityKind()); + fromEI.intoBuilder() + .withDifferentiabilityKind(toEI.getDifferentiabilityKind()) + .build(); fromFunc = FunctionType::get(toFunc->getParams(), fromFunc->getResult()) ->withExtInfo(newEI) ->castTo(); @@ -7119,9 +7283,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto *arg = apply->getArg(); auto *fn = apply->getFn(); - bool hasTrailingClosure = - isa(apply) && cast(apply)->hasTrailingClosure(); - auto finishApplyOfDeclWithSpecialTypeCheckingSemantics = [&](ApplyExpr *apply, ConcreteDeclRef declRef, @@ -7137,7 +7298,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, arg = coerceCallArguments(arg, fnType, declRef, apply, apply->getArgumentLabels(argLabelsScratch), - hasTrailingClosure, locator.withPathElement( ConstraintLocator::ApplyArgument)); if (!arg) { @@ -7341,7 +7501,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, if (auto fnType = cs.getType(fn)->getAs()) { arg = coerceCallArguments(arg, fnType, callee, apply, apply->getArgumentLabels(argLabelsScratch), - hasTrailingClosure, locator.withPathElement( ConstraintLocator::ApplyArgument)); if (!arg) { @@ -7593,9 +7752,8 @@ namespace { componentType = solution.simplifyType(cs.getType(kp, i)); assert(!componentType->hasTypeVariable() && "Should not write type variable into key-path component"); + kp->getMutableComponents()[i].setComponentType(componentType); } - - kp->getMutableComponents()[i].setComponentType(componentType); } } @@ -7640,6 +7798,13 @@ namespace { TapsToTypeCheck.push_back(std::make_pair(tap, Rewriter.dc)); } + if (auto captureList = dyn_cast(expr)) { + // Rewrite captures. + for (const auto &capture : captureList->getCaptureList()) { + (void)rewriteTarget(SolutionApplicationTarget(capture.Init)); + } + } + Rewriter.walkToExprPre(expr); return { true, expr }; } @@ -7684,8 +7849,11 @@ namespace { return true; case SolutionApplicationToFunctionResult::Delay: { - auto closure = cast(fn.getAbstractClosureExpr()); - ClosuresToTypeCheck.push_back(closure); + if (!Rewriter.cs.Options + .contains(ConstraintSystemFlags::LeaveClosureBodyUnchecked)) { + auto closure = cast(fn.getAbstractClosureExpr()); + ClosuresToTypeCheck.push_back(closure); + } return false; } } @@ -7911,7 +8079,7 @@ static Optional applySolutionToForEachStmt( if (forEachStmtInfo.whereExpr) { auto *boolDecl = dc->getASTContext().getBoolDecl(); assert(boolDecl); - Type boolType = boolDecl->getDeclaredType(); + Type boolType = boolDecl->getDeclaredInterfaceType(); assert(boolType); SolutionApplicationTarget whereTarget( @@ -8100,7 +8268,7 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { target.getDeclContext()); if (auto coercedPattern = TypeChecker::coercePatternToType( contextualPattern, patternType, patternOptions)) { - (*caseLabelItem)->setPattern(coercedPattern); + (*caseLabelItem)->setPattern(coercedPattern, /*resolved=*/true); } else { return None; } @@ -8112,7 +8280,7 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { return None; // FIXME: Feels like we could leverage existing code more. - Type boolType = cs.getASTContext().getBoolDecl()->getDeclaredType(); + Type boolType = cs.getASTContext().getBoolDecl()->getDeclaredInterfaceType(); guardExpr = solution.coerceToType( guardExpr, boolType, cs.getConstraintLocator(info.guardExpr)); if (!guardExpr) @@ -8264,6 +8432,7 @@ Optional ConstraintSystem::applySolution( // Visit closures that have non-single expression bodies. bool hadError = false; + for (auto *closure : walker.getClosuresToTypeCheck()) hadError |= TypeChecker::typeCheckClosureBody(closure); @@ -8421,7 +8590,8 @@ SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { case Kind::caseLabelItem: if (auto newPattern = caseLabelItem.caseLabelItem->getPattern()->walk(walker)) { - caseLabelItem.caseLabelItem->setPattern(newPattern); + caseLabelItem.caseLabelItem->setPattern( + newPattern, caseLabelItem.caseLabelItem->isPatternResolved()); } if (auto guardExpr = caseLabelItem.caseLabelItem->getGuardExpr()) { if (auto newGuardExpr = guardExpr->walk(walker)) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 15c1bee1ab58e..46c02ad1b70a2 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -21,50 +21,58 @@ using namespace swift; using namespace constraints; -void ConstraintSystem::inferTransitiveSupertypeBindings( - const llvm::SmallDenseMap - &inferredBindings, - PotentialBindings &bindings) { - auto *typeVar = bindings.TypeVar; - - llvm::SmallVector subtypeOf; - // First, let's collect all of the `subtype` constraints associated +void ConstraintSystem::PotentialBindings::inferTransitiveBindings( + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, + const llvm::SmallDenseMap + &inferredBindings) { + using BindingKind = ConstraintSystem::AllowedBindingKind; + + llvm::SmallVector conversions; + // First, let's collect all of the conversions associated // with this type variable. - llvm::copy_if(bindings.Sources, std::back_inserter(subtypeOf), - [&](const Constraint *constraint) -> bool { - if (constraint->getKind() != ConstraintKind::Subtype) - return false; - - auto rhs = simplifyType(constraint->getSecondType()); - return rhs->getAs() == typeVar; - }); - - if (subtypeOf.empty()) - return; - - // We need to make sure that there are no duplicate bindings in the - // set, other we'll produce multiple identical solutions. - llvm::SmallPtrSet existingTypes; - for (const auto &binding : bindings.Bindings) - existingTypes.insert(binding.BindingType->getCanonicalType()); - - for (auto *constraint : subtypeOf) { + llvm::copy_if( + Sources, std::back_inserter(conversions), + [&](const Constraint *constraint) -> bool { + if (constraint->getKind() != ConstraintKind::Subtype && + constraint->getKind() != ConstraintKind::Conversion && + constraint->getKind() != ConstraintKind::ArgumentConversion && + constraint->getKind() != ConstraintKind::OperatorArgumentConversion) + return false; + + auto rhs = cs.simplifyType(constraint->getSecondType()); + return rhs->getAs() == TypeVar; + }); + + for (auto *constraint : conversions) { auto *tv = - simplifyType(constraint->getFirstType())->getAs(); - if (!tv) + cs.simplifyType(constraint->getFirstType())->getAs(); + if (!tv || tv == TypeVar) continue; auto relatedBindings = inferredBindings.find(tv); if (relatedBindings == inferredBindings.end()) continue; - for (auto &binding : relatedBindings->getSecond().Bindings) { + auto &bindings = relatedBindings->getSecond(); + + // Infer transitive protocol requirements. + llvm::copy(bindings.Protocols, std::back_inserter(Protocols)); + + // Infer transitive defaults. + llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); + + // TODO: We shouldn't need this in the future. + if (constraint->getKind() != ConstraintKind::Subtype) + continue; + + for (auto &binding : bindings.Bindings) { // We need the binding kind for the potential binding to // either be Exact or Supertypes in order for it to make sense // to add Supertype bindings based on the relationship between // our type variables. - if (binding.Kind != AllowedBindingKind::Exact && - binding.Kind != AllowedBindingKind::Supertypes) + if (binding.Kind != BindingKind::Exact && + binding.Kind != BindingKind::Supertypes) continue; auto type = binding.BindingType; @@ -75,12 +83,285 @@ void ConstraintSystem::inferTransitiveSupertypeBindings( if (!existingTypes.insert(type->getCanonicalType()).second) continue; - if (ConstraintSystem::typeVarOccursInType(typeVar, type)) + if (ConstraintSystem::typeVarOccursInType(TypeVar, type)) + continue; + + addPotentialBinding( + binding.withSameSource(type, BindingKind::Supertypes)); + } + } +} + +static bool +isUnviableDefaultType(Type defaultType, + llvm::SmallPtrSetImpl &existingTypes) { + auto canType = defaultType->getCanonicalType(); + + if (!defaultType->hasUnboundGenericType()) + return !existingTypes.insert(canType).second; + + // For generic literal types, check whether we already have a + // specialization of this generic within our list. + // FIXME: This assumes that, e.g., the default literal + // int/float/char/string types are never generic. + auto nominal = defaultType->getAnyNominal(); + if (!nominal) + return true; + + if (llvm::any_of(existingTypes, [&nominal](CanType existingType) { + // FIXME: Check parents? + return nominal == existingType->getAnyNominal(); + })) + return true; + + existingTypes.insert(canType); + return false; +} + +void ConstraintSystem::PotentialBindings::inferDefaultTypes( + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes) { + auto isDirectRequirement = [&](Constraint *constraint) -> bool { + if (auto *typeVar = constraint->getFirstType()->getAs()) { + auto *repr = cs.getRepresentative(typeVar); + return repr == TypeVar; + } + + return false; + }; + + // If we have any literal constraints, check whether there is already a + // binding that provides a type that conforms to that literal protocol. In + // such cases, don't add the default binding suggestion because the existing + // suggestion is better. + // + // Note that ordering is important when it comes to bindings, we'd like to + // add any "direct" default types first to attempt them before transitive + // ones. + // + // Key is a literal protocol requirement, Value indicates whether (first) + // given protocol is a direct requirement, and (second) whether it has been + // covered by an existing binding. + llvm::SmallMapVector, 4> + literalProtocols; + for (auto *constraint : Protocols) { + if (constraint->getKind() == ConstraintKind::LiteralConformsTo) + literalProtocols.insert({constraint->getProtocol(), + {isDirectRequirement(constraint), false}}); + } + + for (auto &binding : Bindings) { + Type type; + + switch (binding.Kind) { + case AllowedBindingKind::Exact: + type = binding.BindingType; + break; + + case AllowedBindingKind::Subtypes: + case AllowedBindingKind::Supertypes: + type = binding.BindingType->getRValueType(); + break; + } + + if (type->isTypeVariableOrMember() || type->isHole()) + continue; + + bool requiresUnwrap = false; + for (auto &entry : literalProtocols) { + auto *protocol = entry.first; + bool isDirectRequirement = entry.second.first; + bool &isCovered = entry.second.second; + + if (isCovered) + continue; + + // FIXME: This is a hack and it's incorrect because it depends + // on ordering of the literal procotols e.g. if `ExpressibleByNilLiteral` + // appears before e.g. `ExpressibleByIntegerLiteral` we'd drop + // optionality although that would be incorrect. + do { + // If the type conforms to this protocol, we're covered. + if (TypeChecker::conformsToProtocol(type, protocol, cs.DC)) { + isCovered = true; + break; + } + + // If this literal protocol is not a direct requirement it + // would be possible to change optionality while inferring + // bindings for a supertype, so this hack doesn't apply. + if (!isDirectRequirement) + break; + + // If we're allowed to bind to subtypes, look through optionals. + // FIXME: This is really crappy special case of computing a reasonable + // result based on the given constraints. + if (binding.Kind == AllowedBindingKind::Subtypes) { + if (auto objTy = type->getOptionalObjectType()) { + requiresUnwrap = true; + type = objTy; + continue; + } + } + + requiresUnwrap = false; + break; + } while (true); + } + + if (requiresUnwrap) + binding.BindingType = type; + } + + // If this is not a literal protocol or it has been "covered" by an existing + // binding it can't provide a default type. + auto isUnviableForDefaulting = [&literalProtocols](ProtocolDecl *protocol) { + auto literal = literalProtocols.find(protocol); + return literal == literalProtocols.end() || literal->second.second; + }; + + for (auto *constraint : Protocols) { + auto *protocol = constraint->getProtocol(); + + if (isUnviableForDefaulting(protocol)) + continue; + + // Let's try to coalesce integer and floating point literal protocols + // if they appear together because the only possible default type that + // could satisfy both requirements is `Double`. + if (protocol->isSpecificProtocol( + KnownProtocolKind::ExpressibleByIntegerLiteral)) { + auto *floatLiteral = cs.getASTContext().getProtocol( + KnownProtocolKind::ExpressibleByFloatLiteral); + // If `ExpressibleByFloatLiteral` is a requirement and it isn't + // covered, let's skip `ExpressibleByIntegerLiteral` requirement. + if (!isUnviableForDefaulting(floatLiteral)) continue; + } + + auto defaultType = TypeChecker::getDefaultType(protocol, cs.DC); + if (!defaultType) + continue; - bindings.addPotentialBinding( - binding.withSameSource(type, AllowedBindingKind::Supertypes)); + if (isUnviableDefaultType(defaultType, existingTypes)) + continue; + + // We need to figure out whether this is a direct conformance + // requirement or inferred transitive one to identify binding + // kind correctly. + addPotentialBinding({defaultType, + isDirectRequirement(constraint) + ? AllowedBindingKind::Subtypes + : AllowedBindingKind::Supertypes, + constraint}); + } + + /// Add defaultable constraints. + for (auto *constraint : Defaults) { + Type type = constraint->getSecondType(); + if (!existingTypes.insert(type->getCanonicalType()).second) + continue; + + if (constraint->getKind() == ConstraintKind::DefaultClosureType) { + // If there are no other possible bindings for this closure + // let's default it to the type inferred from its parameters/body, + // otherwise we should only attempt contextual types as a + // top-level closure type. + if (!Bindings.empty()) + continue; } + + addPotentialBinding({type, AllowedBindingKind::Exact, constraint}); + } +} + +void ConstraintSystem::PotentialBindings::finalize( + ConstraintSystem &cs, + const llvm::SmallDenseMap + &inferredBindings) { + // We need to make sure that there are no duplicate bindings in the + // set, otherwise solver would produce multiple identical solutions. + llvm::SmallPtrSet existingTypes; + for (const auto &binding : Bindings) + existingTypes.insert(binding.BindingType->getCanonicalType()); + + inferTransitiveBindings(cs, existingTypes, inferredBindings); + + inferDefaultTypes(cs, existingTypes); + + // Adjust optionality of existing bindings based on presence of + // `ExpressibleByNilLiteral` requirement. + if (llvm::any_of(Protocols, [](Constraint *constraint) { + auto *protocol = constraint->getProtocol(); + return protocol->isSpecificProtocol( + KnownProtocolKind::ExpressibleByNilLiteral); + })) { + for (auto &binding : Bindings) { + bool wrapInOptional = false; + if (binding.Kind == AllowedBindingKind::Supertypes) { + auto type = binding.BindingType->getRValueType(); + // If the type doesn't conform to ExpressibleByNilLiteral, + // produce an optional of that type as a potential binding. We + // overwrite the binding in place because the non-optional type + // will fail to type-check against the nil-literal conformance. + bool conformsToExprByNilLiteral = false; + if (auto *nominalBindingDecl = type->getAnyNominal()) { + SmallVector conformances; + conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance( + cs.DC->getParentModule(), + cs.getASTContext().getProtocol( + KnownProtocolKind::ExpressibleByNilLiteral), + conformances); + } + wrapInOptional = !conformsToExprByNilLiteral; + } else if (binding.isDefaultableBinding() && + binding.BindingType->isAny()) { + wrapInOptional = true; + } + + if (wrapInOptional) + binding.BindingType = OptionalType::get(binding.BindingType); + } + } + + // If there are no bindings, typeVar may be a hole. + if (cs.shouldAttemptFixes() && Bindings.empty() && + TypeVar->getImpl().canBindToHole()) { + IsHole = true; + // If the base of the unresolved member reference like `.foo` + // couldn't be resolved we'd want to bind it to a hole at the + // very last moment possible, just like generic parameters. + auto *locator = TypeVar->getImpl().getLocator(); + if (locator->isLastElement()) + PotentiallyIncomplete = true; + + addPotentialBinding(PotentialBinding::forHole(cs.getASTContext(), locator)); + } + + // Let's always consider `Any` to be a last resort binding because + // it's always better to infer concrete type and erase it if required + // by the context. + if (Bindings.size() > 1) { + auto AnyTypePos = + llvm::find_if(Bindings, [](const PotentialBinding &binding) { + return binding.BindingType->isAny() && + !binding.isDefaultableBinding(); + }); + + if (AnyTypePos != Bindings.end()) { + std::rotate(AnyTypePos, AnyTypePos + 1, Bindings.end()); + } + } + + // Determine if the bindings only constrain the type variable from above with + // an existential type; such a binding is not very helpful because it's + // impossible to enumerate the existential type's subtypes. + if (!Bindings.empty()) { + SubtypeOfExistentialType = + llvm::all_of(Bindings, [](const PotentialBinding &binding) { + return binding.BindingType->isExistentialType() && + binding.Kind == AllowedBindingKind::Subtypes; + }); } } @@ -92,13 +373,32 @@ ConstraintSystem::determineBestBindings() { // First, let's collect all of the possible bindings. for (auto *typeVar : getTypeVariables()) { - if (typeVar->getImpl().hasRepresentativeOrFixed()) - continue; - - if (auto bindings = getPotentialBindings(typeVar)) - cache.insert({typeVar, std::move(bindings)}); + if (!typeVar->getImpl().hasRepresentativeOrFixed()) + cache.insert({typeVar, getPotentialBindings(typeVar)}); } + // Determine whether given type variable with its set of bindings is + // viable to be attempted on the next step of the solver. If type variable + // has no "direct" bindings of any kind e.g. direct bindings to concrete + // types, default types from "defaultable" constraints or literal + // conformances, such type variable is not viable to be evaluated to be + // attempted next. + auto isViableForRanking = + [this](const ConstraintSystem::PotentialBindings &bindings) -> bool { + auto *typeVar = bindings.TypeVar; + + // If type variable is marked as a potential hole there is always going + // to be at least one binding available for it. + if (shouldAttemptFixes() && typeVar->getImpl().canBindToHole()) + return true; + + return !bindings.Bindings.empty() || !bindings.Defaults.empty() || + llvm::any_of(bindings.Protocols, [&](Constraint *constraint) { + return bool( + TypeChecker::getDefaultType(constraint->getProtocol(), DC)); + }); + }; + // Now let's see if we could infer something for related type // variables based on other bindings. for (auto *typeVar : getTypeVariables()) { @@ -108,7 +408,22 @@ ConstraintSystem::determineBestBindings() { auto &bindings = cachedBindings->getSecond(); - inferTransitiveSupertypeBindings(cache, bindings); + // Before attempting to infer transitive bindings let's check + // whether there are any viable "direct" bindings associated with + // current type variable, if there are none - it means that this type + // variable could only be used to transitively infer bindings for + // other type variables and can't participate in ranking. + // + // Viable bindings include - any types inferred from constraints + // associated with given type variable, any default constraints, + // or any conformance requirements to literal protocols with can + // produce a default type. + bool isViable = isViableForRanking(bindings); + + bindings.finalize(*this, cache); + + if (!bindings || !isViable) + continue; if (isDebugMode()) { bindings.dump(typeVar, llvm::errs(), solverState->depth * 2); @@ -261,31 +576,21 @@ bool ConstraintSystem::PotentialBindings::favoredOverDisjunction( return !InvolvesTypeVariables; } -static bool hasNilLiteralConstraint(TypeVariableType *typeVar, - const ConstraintSystem &CS) { - // Look for a literal-conformance constraint on the type variable. - auto constraints = - CS.getConstraintGraph().gatherConstraints( - typeVar, ConstraintGraph::GatheringKind::EquivalenceClass, - [](Constraint *constraint) -> bool { - return constraint->getKind() == ConstraintKind::LiteralConformsTo && - constraint->getProtocol()->isSpecificProtocol( - KnownProtocolKind::ExpressibleByNilLiteral); - }); - - for (auto constraint : constraints) - if (CS.simplifyType(constraint->getFirstType())->isEqual(typeVar)) - return true; +ConstraintSystem::PotentialBindings +ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar) { + auto bindings = getPotentialBindings(typeVar); - return false; + llvm::SmallDenseMap + inferred; + bindings.finalize(*this, inferred); + return bindings; } Optional ConstraintSystem::getPotentialBindingForRelationalConstraint( PotentialBindings &result, Constraint *constraint, bool &hasDependentMemberRelationalConstraints, - bool &hasNonDependentMemberRelationalConstraints, - bool &addOptionalSupertypeBindings) const { + bool &hasNonDependentMemberRelationalConstraints) const { assert(constraint->getClassification() == ConstraintClassification::Relational && "only relational constraints handled here"); @@ -405,15 +710,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( result.SubtypeOf.insert(bindingTypeVar); } - // If we've already set addOptionalSupertypeBindings, or we aren't - // allowing supertype bindings, we're done. - if (addOptionalSupertypeBindings || kind != AllowedBindingKind::Supertypes) - return None; - - // If the bound is a 'nil' literal type, add optional supertype bindings. - if (hasNilLiteralConstraint(bindingTypeVar, *this)) - addOptionalSupertypeBindings = true; - return None; } @@ -474,9 +770,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { // Consider each of the constraints related to this type variable. llvm::SmallPtrSet exactTypes; - SmallVector defaultableConstraints; SmallVector literalBindings; - bool addOptionalSupertypeBindings = false; bool hasNonDependentMemberRelationalConstraints = false; bool hasDependentMemberRelationalConstraints = false; for (auto constraint : constraints) { @@ -506,8 +800,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { auto binding = getPotentialBindingForRelationalConstraint( result, constraint, hasDependentMemberRelationalConstraints, - hasNonDependentMemberRelationalConstraints, - addOptionalSupertypeBindings); + hasNonDependentMemberRelationalConstraints); if (!binding) break; @@ -596,7 +889,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { // Do these in a separate pass. if (getFixedTypeRecursive(constraint->getFirstType(), true) ->getAs() == typeVar) { - defaultableConstraints.push_back(constraint); + result.Defaults.push_back(constraint); hasNonDependentMemberRelationalConstraints = true; } break; @@ -628,56 +921,10 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { LLVM_FALLTHROUGH; case ConstraintKind::LiteralConformsTo: { - // If there is a 'nil' literal constraint, we might need optional - // supertype bindings. - if (constraint->getProtocol()->isSpecificProtocol( - KnownProtocolKind::ExpressibleByNilLiteral)) { - addOptionalSupertypeBindings = true; - } - - // If there is a default literal type for this protocol, it's a - // potential binding. - auto defaultType = TypeChecker::getDefaultType(constraint->getProtocol(), DC); - if (!defaultType) - continue; - + // Record constraint where protocol requirement originated + // this is useful to use for the binding later. + result.Protocols.push_back(constraint); hasNonDependentMemberRelationalConstraints = true; - - // Handle unspecialized types directly. - if (!defaultType->hasUnboundGenericType()) { - if (!exactTypes.insert(defaultType->getCanonicalType()).second) - continue; - - literalBindings.push_back( - {defaultType, AllowedBindingKind::Subtypes, constraint}); - continue; - } - - // For generic literal types, check whether we already have a - // specialization of this generic within our list. - // FIXME: This assumes that, e.g., the default literal - // int/float/char/string types are never generic. - auto nominal = defaultType->getAnyNominal(); - if (!nominal) - continue; - - bool matched = false; - for (auto exactType : exactTypes) { - if (auto exactNominal = exactType->getAnyNominal()) { - // FIXME: Check parents? - if (nominal == exactNominal) { - matched = true; - break; - } - } - } - - if (!matched) { - exactTypes.insert(defaultType->getCanonicalType()); - literalBindings.push_back( - {defaultType, AllowedBindingKind::Subtypes, constraint}); - } - break; } @@ -743,157 +990,6 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { } } - // If we have any literal constraints, check whether there is already a - // binding that provides a type that conforms to that literal protocol. In - // such cases, remove the default binding suggestion because the existing - // suggestion is better. - if (!literalBindings.empty()) { - SmallPtrSet coveredLiteralProtocols; - for (auto &binding : result.Bindings) { - Type testType; - - switch (binding.Kind) { - case AllowedBindingKind::Exact: - testType = binding.BindingType; - break; - - case AllowedBindingKind::Subtypes: - case AllowedBindingKind::Supertypes: - testType = binding.BindingType->getRValueType(); - break; - } - - // Attempting to check conformance of the type variable, - // or unresolved type is invalid since it would result - // in lose of viable literal bindings because that check - // always returns trivial conformance. - if (testType->isTypeVariableOrMember() || testType->is()) - continue; - - // Check each non-covered literal protocol to determine which ones - // might be covered by non-defaulted bindings. - bool updatedBindingType = false; - for (auto &literalBinding : literalBindings) { - auto *protocol = literalBinding.getDefaultedLiteralProtocol(); - - assert(protocol); - - // Has already been covered by one of the bindings. - if (coveredLiteralProtocols.count(protocol)) - continue; - - do { - // If the type conforms to this protocol, we're covered. - if (DC->getParentModule()->lookupConformance(testType, protocol)) { - coveredLiteralProtocols.insert(protocol); - break; - } - - // If we're allowed to bind to subtypes, look through optionals. - // FIXME: This is really crappy special case of computing a reasonable - // result based on the given constraints. - if (binding.Kind == AllowedBindingKind::Subtypes) { - if (auto objTy = testType->getOptionalObjectType()) { - updatedBindingType = true; - testType = objTy; - continue; - } - } - - updatedBindingType = false; - break; - } while (true); - } - - if (updatedBindingType) - binding.BindingType = testType; - } - - for (auto &literalBinding : literalBindings) { - auto *protocol = literalBinding.getDefaultedLiteralProtocol(); - // For any literal type that has been covered, skip them. - if (coveredLiteralProtocols.count(protocol) == 0) - result.addPotentialBinding(std::move(literalBinding)); - } - } - - /// Add defaultable constraints last. - for (auto constraint : defaultableConstraints) { - Type type = constraint->getSecondType(); - if (!exactTypes.insert(type->getCanonicalType()).second) - continue; - - if (constraint->getKind() == ConstraintKind::DefaultClosureType) { - // If there are no other possible bindings for this closure - // let's default it to the type inferred from its parameters/body, - // otherwise we should only attempt contextual types as a - // top-level closure type. - if (!result.Bindings.empty()) - continue; - } - - result.addPotentialBinding({type, AllowedBindingKind::Exact, constraint}); - } - - // If there are no bindings, typeVar may be a hole. - if (shouldAttemptFixes() && result.Bindings.empty() && - typeVar->getImpl().canBindToHole()) { - result.IsHole = true; - // If the base of the unresolved member reference like `.foo` - // couldn't be resolved we'd want to bind it to a hole at the - // very last moment possible, just like generic parameters. - auto *locator = typeVar->getImpl().getLocator(); - if (locator->isLastElement()) - result.PotentiallyIncomplete = true; - - result.addPotentialBinding( - PotentialBinding::forHole(getASTContext(), locator)); - } - - // Determine if the bindings only constrain the type variable from above with - // an existential type; such a binding is not very helpful because it's - // impossible to enumerate the existential type's subtypes. - result.SubtypeOfExistentialType = - std::all_of(result.Bindings.begin(), result.Bindings.end(), - [](const PotentialBinding &binding) { - return binding.BindingType->isExistentialType() && - binding.Kind == AllowedBindingKind::Subtypes; - }); - - // If we're supposed to add optional supertype bindings, do so now. - if (addOptionalSupertypeBindings) { - for (unsigned i : indices(result.Bindings)) { - auto &binding = result.Bindings[i]; - bool wrapInOptional = false; - - if (binding.Kind == AllowedBindingKind::Supertypes) { - // If the type doesn't conform to ExpressibleByNilLiteral, - // produce an optional of that type as a potential binding. We - // overwrite the binding in place because the non-optional type - // will fail to type-check against the nil-literal conformance. - auto nominalBindingDecl = - binding.BindingType->getRValueType()->getAnyNominal(); - bool conformsToExprByNilLiteral = false; - if (nominalBindingDecl) { - SmallVector conformances; - conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance( - DC->getParentModule(), - getASTContext().getProtocol( - KnownProtocolKind::ExpressibleByNilLiteral), - conformances); - } - wrapInOptional = !conformsToExprByNilLiteral; - } else if (binding.isDefaultableBinding() && - binding.BindingType->isAny()) { - wrapInOptional = true; - } - - if (wrapInOptional) { - binding.BindingType = OptionalType::get(binding.BindingType); - } - } - } - // If there were both dependent-member and non-dependent-member relational // constraints, consider this "fully bound"; we don't want to touch it. if (hasDependentMemberRelationalConstraints) { diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 0fd1b69529a54..63984007b63ac 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -345,11 +345,13 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( ClosureConstraintApplication application( solution, closure, closureFnType->getResult(), rewriteTarget); application.visit(fn.getBody()); + closure->setBodyState(ClosureExpr::BodyState::TypeCheckedWithSignature); return SolutionApplicationToFunctionResult::Success; } // Otherwise, we need to delay type checking of the closure until later. solution.setExprTypes(closure); + closure->setBodyState(ClosureExpr::BodyState::ReadyForTypeChecking); return SolutionApplicationToFunctionResult::Delay; } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index c3037a2053d6a..ee0a332e702d8 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -240,6 +240,13 @@ ValueDecl *RequirementFailure::getDeclRef() const { return type->getAnyGeneric(); }; + // If the locator is for a function builder body result type, the requirement + // came from the function's return type. + if (getLocator()->isForFunctionBuilderBodyResult()) { + auto *func = getAsDecl(getAnchor()); + return getAffectedDeclFromType(func->getResultInterfaceType()); + } + if (isFromContextualType()) return getAffectedDeclFromType(getContextualType(getRawAnchor())); @@ -908,12 +915,21 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const { // Give a note and fix-it auto note = emitDiagnosticAt(PD, diag::noescape_parameter, PD->getName()); + SourceLoc reprLoc; + SourceLoc autoclosureEndLoc; + if (auto *repr = PD->getTypeRepr()) { + reprLoc = repr->getStartLoc(); + if (auto *attrRepr = dyn_cast(repr)) { + autoclosureEndLoc = Lexer::getLocForEndOfToken( + getASTContext().SourceMgr, + attrRepr->getAttrs().getLoc(TAK_autoclosure)); + } + } if (!PD->isAutoClosure()) { - SourceLoc reprLoc; - if (auto *repr = PD->getTypeRepr()) - reprLoc = repr->getStartLoc(); note.fixItInsert(reprLoc, "@escaping "); - } // TODO: add in a fixit for autoclosure + } else { + note.fixItInsertAfter(autoclosureEndLoc, " @escaping"); + } return true; } @@ -972,8 +988,6 @@ bool MissingExplicitConversionFailure::diagnoseAsError() { return false; bool useAs = TypeChecker::isExplicitlyConvertibleTo(fromType, toType, DC); - if (!useAs && !TypeChecker::checkedCastMaySucceed(fromType, toType, DC)) - return false; auto *expr = findParentExpr(anchor); if (!expr) @@ -2172,6 +2186,11 @@ bool ContextualFailure::diagnoseAsError() { return true; } + case ConstraintLocator::FunctionBuilderBodyResult: { + diagnostic = *getDiagnosticFor(CTP_Initialization, toType); + break; + } + default: return false; } @@ -3915,9 +3934,9 @@ bool MissingArgumentsFailure::diagnoseAsError() { Expr *fnExpr = nullptr; Expr *argExpr = nullptr; unsigned numArguments = 0; - bool hasTrailingClosure = false; + Optional firstTrailingClosure = None; - std::tie(fnExpr, argExpr, numArguments, hasTrailingClosure) = + std::tie(fnExpr, argExpr, numArguments, firstTrailingClosure) = getCallInfo(getRawAnchor()); // TODO(diagnostics): We should be able to suggest this fix-it @@ -3980,46 +3999,66 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { auto position = argument.first; auto label = argument.second.getLabel(); - SmallString<32> insertBuf; - llvm::raw_svector_ostream insertText(insertBuf); - - if (position != 0) - insertText << ", "; - - forFixIt(insertText, argument.second); - Expr *fnExpr = nullptr; Expr *argExpr = nullptr; - unsigned insertableEndIdx = 0; - bool hasTrailingClosure = false; + unsigned numArgs = 0; + Optional firstTrailingClosure = None; - std::tie(fnExpr, argExpr, insertableEndIdx, hasTrailingClosure) = + std::tie(fnExpr, argExpr, numArgs, firstTrailingClosure) = getCallInfo(anchor); - if (!argExpr) + if (!argExpr) { return false; + } + + // Will the parameter accept a trailing closure? + Type paramType = resolveType(argument.second.getPlainType()); + bool paramAcceptsTrailingClosure = paramType + ->lookThroughAllOptionalTypes()->is(); + + // Determine whether we're inserting as a trailing closure. + bool insertingTrailingClosure = + firstTrailingClosure && position > *firstTrailingClosure; - if (hasTrailingClosure) - insertableEndIdx -= 1; + SmallString<32> insertBuf; + llvm::raw_svector_ostream insertText(insertBuf); - if (position == 0 && insertableEndIdx != 0) + if (insertingTrailingClosure) + insertText << " "; + else if (position != 0) + insertText << ", "; + + forFixIt(insertText, argument.second); + + if (position == 0 && numArgs > 0 && + (!firstTrailingClosure || position < *firstTrailingClosure)) insertText << ", "; SourceLoc insertLoc; - if (auto *TE = dyn_cast(argExpr)) { - // fn(): - // fn([argMissing]) + + if (position >= numArgs && insertingTrailingClosure) { + // Add a trailing closure to the end. + + // fn { closure }: + // fn {closure} label: [argMissing] + // fn() { closure }: + // fn() {closure} label: [argMissing] + // fn(argX) { closure }: + // fn(argX) { closure } label: [argMissing] + insertLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, argExpr->getEndLoc()); + } else if (auto *TE = dyn_cast(argExpr)) { // fn(argX, argY): // fn([argMissing, ]argX, argY) // fn(argX[, argMissing], argY) - // fn(argX, argY[, argMissing]) // fn(argX) { closure }: // fn([argMissing, ]argX) { closure } // fn(argX[, argMissing]) { closure } - // fn(argX[, closureLabel: ]{closure}[, argMissing)] // Not impl. - if (insertableEndIdx == 0) + // fn(argX, argY): + // fn(argX, argY[, argMissing]) + if (numArgs == 0) { insertLoc = TE->getRParenLoc(); - else if (position != 0) { + } else if (position != 0) { auto argPos = std::min(TE->getNumElements(), position) - 1; insertLoc = Lexer::getLocForEndOfToken( ctx.SourceMgr, TE->getElement(argPos)->getEndLoc()); @@ -4031,25 +4070,25 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { } else { auto *PE = cast(argExpr); if (PE->getRParenLoc().isValid()) { + // fn(): + // fn([argMissing]) // fn(argX): - // fn([argMissing, ]argX) // fn(argX[, argMissing]) + // fn([argMissing, ]argX) // fn() { closure }: // fn([argMissing]) {closure} - // fn([closureLabel: ]{closure}[, argMissing]) // Not impl. - if (insertableEndIdx == 0) - insertLoc = PE->getRParenLoc(); - else if (position == 0) - insertLoc = PE->getSubExpr()->getStartLoc(); - else + if (position == 0) { insertLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, - PE->getSubExpr()->getEndLoc()); + PE->getLParenLoc()); + } else { + insertLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, PE->getSubExpr()->getEndLoc()); + } } else { // fn { closure }: // fn[(argMissing)] { closure } - // fn[(closureLabel:] { closure }[, missingArg)] // Not impl. assert(!isExpr(anchor) && "bracket less subscript"); - assert(PE->hasTrailingClosure() && + assert(firstTrailingClosure && "paren less ParenExpr without trailing closure"); insertBuf.insert(insertBuf.begin(), '('); insertBuf.insert(insertBuf.end(), ')'); @@ -4061,16 +4100,28 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { if (insertLoc.isInvalid()) return false; + // If we are trying to insert a trailing closure but the parameter + // corresponding to the missing argument doesn't support a trailing closure, + // don't provide a Fix-It. + // FIXME: It's possible to parenthesize and relabel the argument list to + // accomodate this, but it's tricky. + bool shouldEmitFixIt = + !(insertingTrailingClosure && !paramAcceptsTrailingClosure); + if (label.empty()) { - emitDiagnosticAt(insertLoc, diag::missing_argument_positional, position + 1) - .fixItInsert(insertLoc, insertText.str()); + auto diag = emitDiagnosticAt( + insertLoc, diag::missing_argument_positional, position + 1); + if (shouldEmitFixIt) + diag.fixItInsert(insertLoc, insertText.str()); } else if (isPropertyWrapperInitialization()) { auto *TE = cast(fnExpr); emitDiagnosticAt(TE->getLoc(), diag::property_wrapper_missing_arg_init, label, resolveType(TE->getInstanceType())->getString()); } else { - emitDiagnosticAt(insertLoc, diag::missing_argument_named, label) - .fixItInsert(insertLoc, insertText.str()); + auto diag = emitDiagnosticAt( + insertLoc, diag::missing_argument_named, label); + if (shouldEmitFixIt) + diag.fixItInsert(insertLoc, insertText.str()); } if (auto selectedOverload = @@ -4298,23 +4349,24 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument( return TypeChecker::isConvertibleTo(argType, paramType, solution.getDC()); } -std::tuple +std::tuple> MissingArgumentsFailure::getCallInfo(ASTNode anchor) const { if (auto *call = getAsExpr(anchor)) { return std::make_tuple(call->getFn(), call->getArg(), - call->getNumArguments(), call->hasTrailingClosure()); + call->getNumArguments(), + call->getUnlabeledTrailingClosureIndex()); } else if (auto *UME = getAsExpr(anchor)) { return std::make_tuple(UME, UME->getArgument(), UME->getNumArguments(), - UME->hasTrailingClosure()); + UME->getUnlabeledTrailingClosureIndex()); } else if (auto *SE = getAsExpr(anchor)) { return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(), - SE->hasTrailingClosure()); + SE->getUnlabeledTrailingClosureIndex()); } else if (auto *OLE = getAsExpr(anchor)) { return std::make_tuple(OLE, OLE->getArg(), OLE->getNumArguments(), - OLE->hasTrailingClosure()); + OLE->getUnlabeledTrailingClosureIndex()); } - return std::make_tuple(nullptr, nullptr, 0, false); + return std::make_tuple(nullptr, nullptr, 0, None); } void MissingArgumentsFailure::forFixIt( @@ -4902,6 +4954,9 @@ bool CollectionElementContextualFailure::diagnoseAsError() { Optional diagnostic; if (isExpr(anchor)) { + if (diagnoseMergedLiteralElements()) + return true; + diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element, eltType, contextualType)); } @@ -4952,6 +5007,30 @@ bool CollectionElementContextualFailure::diagnoseAsError() { return true; } +bool CollectionElementContextualFailure::diagnoseMergedLiteralElements() { + auto elementAnchor = simplifyLocatorToAnchor(getLocator()); + if (!elementAnchor) + return false; + + auto *typeVar = getRawType(elementAnchor)->getAs(); + if (!typeVar || !typeVar->getImpl().getAtomicLiteralKind()) + return false; + + // This element is a literal whose type variable could have been merged with others, + // but the conversion constraint to the array element type was only placed on one + // of them. So, we want to emit the error for each element whose type variable is in + // this equivalence class. + auto &cs = getConstraintSystem(); + auto node = cs.getRepresentative(typeVar)->getImpl().getGraphNode(); + for (const auto *typeVar : node->getEquivalenceClass()) { + auto anchor = typeVar->getImpl().getLocator()->getAnchor(); + emitDiagnosticAt(constraints::getLoc(anchor), diag::cannot_convert_array_element, + getFromType(), getToType()); + } + + return true; +} + bool MissingContextualConformanceFailure::diagnoseAsError() { auto anchor = getAnchor(); auto path = getLocator()->getPath(); @@ -5458,7 +5537,7 @@ void InOutConversionFailure::fixItChangeArgumentType() const { SourceLoc startLoc; // Left invalid if we're inserting auto isSimpleTypelessPattern = [](Pattern *P) -> bool { - if (auto VP = dyn_cast_or_null(P)) + if (auto VP = dyn_cast_or_null(P)) P = VP->getSubPattern(); return P && isa(P); }; @@ -5509,6 +5588,9 @@ bool ArgumentMismatchFailure::diagnoseAsError() { if (diagnosePropertyWrapperMismatch()) return true; + if (diagnoseTrailingClosureMismatch()) + return true; + auto argType = getFromType(); auto paramType = getToType(); @@ -5743,6 +5825,26 @@ bool ArgumentMismatchFailure::diagnosePropertyWrapperMismatch() const { return true; } +bool ArgumentMismatchFailure::diagnoseTrailingClosureMismatch() const { + if (!Info.isTrailingClosure()) + return false; + + auto paramType = getToType(); + if (paramType->lookThroughAllOptionalTypes()->is()) + return false; + + emitDiagnostic(diag::trailing_closure_bad_param, paramType) + .highlight(getSourceRange()); + + if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) { + if (auto *decl = overload->choice.getDeclOrNull()) { + emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName()); + } + } + + return true; +} + void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt( const Expr *anchor) const { // If this is an array literal, offer to remove the brackets and pass the @@ -5832,19 +5934,6 @@ bool ExtraneousCallFailure::diagnoseAsError() { return true; } -bool InvalidUseOfTrailingClosure::diagnoseAsError() { - emitDiagnostic(diag::trailing_closure_bad_param, getToType()) - .highlight(getSourceRange()); - - if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) { - if (auto *decl = overload->choice.getDeclOrNull()) { - emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName()); - } - } - - return true; -} - void NonEphemeralConversionFailure::emitSuggestionNotes() const { auto getPointerKind = [](Type ty) -> PointerTypeKind { PointerTypeKind pointerKind; @@ -6493,3 +6582,19 @@ void MissingRawValueFailure::fixIt(InFlightDiagnostic &diagnostic) const { diagnostic.fixItInsertAfter(range.End, fix); } + +bool MissingOptionalUnwrapKeyPathFailure::diagnoseAsError() { + emitDiagnostic(diag::optional_not_unwrapped, getFromType(), + getFromType()->lookThroughSingleOptionalType()); + + emitDiagnostic(diag::optional_keypath_application_base) + .fixItInsertAfter(getLoc(), "?"); + emitDiagnostic(diag::unwrap_with_force_value) + .fixItInsertAfter(getLoc(), "!"); + return true; +} + +SourceLoc MissingOptionalUnwrapKeyPathFailure::getLoc() const { + auto *SE = castToExpr(getAnchor()); + return SE->getBase()->getEndLoc(); +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 70952112677e7..fd84a2be78015 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -785,7 +785,7 @@ class ThrowingFunctionConversionFailure final : public ContextualFailure { : ContextualFailure(solution, fromType, toType, locator) { auto fnType1 = fromType->castTo(); auto fnType2 = toType->castTo(); - assert(fnType1->throws() != fnType2->throws()); + assert(fnType1->isThrowing() != fnType2->isThrowing()); } bool diagnoseAsError() override; @@ -1293,9 +1293,10 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool isPropertyWrapperInitialization() const; /// Gather information associated with expression that represents - /// a call - function, arguments, # of arguments and whether it has - /// a trailing closure. - std::tuple getCallInfo(ASTNode anchor) const; + /// a call - function, arguments, # of arguments and the position of + /// the first trailing closure. + std::tuple> + getCallInfo(ASTNode anchor) const; /// Transform given argument into format suitable for a fix-it /// text e.g. `[